diff --git a/.github/actions/download-sdk-runtime/action.yml b/.github/actions/download-sdk-runtime/action.yml new file mode 100644 index 0000000000..d17f6c5e56 --- /dev/null +++ b/.github/actions/download-sdk-runtime/action.yml @@ -0,0 +1,34 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +name: "Download SDK runtime" +description: "Download an SDK runtime artifact with at most three attempts; fail after exhaustion." +inputs: + artifact-name: + description: "Name of the target-specific runtime artifact." + required: true + path: + description: "Directory in which to download the artifact." + required: true +runs: + using: "composite" + steps: + - name: Download runtime artifact (attempt 1/3) + id: download-runtime + uses: actions/download-artifact@v8 + continue-on-error: true + with: + name: ${{ inputs.artifact-name }} + path: ${{ inputs.path }} + - name: Download runtime artifact (attempt 2/3) + id: download-runtime-retry-1 + if: steps.download-runtime.outcome == 'failure' + uses: actions/download-artifact@v8 + continue-on-error: true + with: + name: ${{ inputs.artifact-name }} + path: ${{ inputs.path }} + - name: Download runtime artifact (attempt 3/3) + if: steps.download-runtime.outcome == 'failure' && steps.download-runtime-retry-1.outcome == 'failure' + uses: actions/download-artifact@v8 + with: + name: ${{ inputs.artifact-name }} + path: ${{ inputs.path }} diff --git a/.github/actions/run-alpine-tests/action.yml b/.github/actions/run-alpine-tests/action.yml index 9dbb5e0ac4..a9842c11be 100644 --- a/.github/actions/run-alpine-tests/action.yml +++ b/.github/actions/run-alpine-tests/action.yml @@ -1,5 +1,5 @@ # Runs an SDK command inside x64 Alpine and verifies the container is -# executing on musl with the requested transport. +# executing on musl. name: "Run Alpine SDK Tests" description: "Run an SDK test command in an x64 Alpine container." inputs: @@ -13,22 +13,18 @@ inputs: description: "SDK root inside the container." required: false default: "/workspace" - transport: - description: "SDK transport to test: default or inprocess." - required: true command: description: "Language-specific setup and test command." required: true runs: using: "composite" steps: - - name: Run ${{ inputs.transport }} tests in Alpine + - name: Run SDK tests in Alpine shell: bash env: ALPINE_TEST_COMMAND: ${{ inputs.command }} ALPINE_TEST_IMAGE: ${{ inputs.image }} ALPINE_TEST_SDK_ROOT: ${{ inputs.sdk-root }} - ALPINE_TEST_TRANSPORT: ${{ inputs.transport }} ALPINE_TEST_WORKDIR: ${{ inputs.workdir }} run: | script_path=$(mktemp "$RUNNER_TEMP/copilot-sdk-alpine-test.XXXXXX") @@ -43,16 +39,11 @@ runs: test "$(uname -m)" = "x86_64" ldd --version 2>&1 | grep -qi musl - case "$COPILOT_SDK_TEST_TRANSPORT" in - inprocess) export COPILOT_SDK_DEFAULT_CONNECTION=inprocess ;; - default) unset COPILOT_SDK_DEFAULT_CONNECTION ;; - *) echo "Unknown transport: $COPILOT_SDK_TEST_TRANSPORT" >&2; exit 1 ;; - esac SCRIPT printf '%s\n' "$ALPINE_TEST_COMMAND" >> "$script_path" chmod +x "$script_path" - docker run --rm \ + docker run --rm --init \ --platform linux/amd64 \ --volume "$GITHUB_WORKSPACE:/workspace" \ --volume "$script_path:/tmp/copilot-sdk-alpine-test.sh:ro" \ @@ -68,6 +59,5 @@ runs: --env RUST_BACKTRACE \ --env RUSTDOCFLAGS \ --env RUSTFLAGS \ - --env COPILOT_SDK_TEST_TRANSPORT="$ALPINE_TEST_TRANSPORT" \ "$ALPINE_TEST_IMAGE" \ /bin/sh /tmp/copilot-sdk-alpine-test.sh diff --git a/.github/skills/sdk-java-e2e-test/SKILL.md b/.github/skills/sdk-java-e2e-test/SKILL.md index 7ea8f277d9..1e074c027d 100644 --- a/.github/skills/sdk-java-e2e-test/SKILL.md +++ b/.github/skills/sdk-java-e2e-test/SKILL.md @@ -9,17 +9,12 @@ description: "Use this skill when creating a Java SDK surface-area E2E integrati These instructions work in both the runtime monorepo and the standalone public SDK repository. From the repository root, use `src/sdk` as the SDK root when -that directory exists; otherwise use the repository root itself. All paths and -commands below are relative to that SDK root, and command examples should be -run from it. See the companion `examples.md` for complete snapshot and test -examples. - -This skill covers the complete workflow for adding a new Java failsafe -integration test backed by a handcrafted YAML snapshot for the replay proxy. -Use it only when the new E2E tests Java SDK surface area; shared runtime -functionality available entirely through the SDK should instead be covered -in the TypeScript SDK suite (`nodejs/test/e2e/` under the SDK root, or -`src/sdk/nodejs/test/e2e/` from the runtime repository root). +that directory exists; otherwise use the repository root itself. All paths below +are relative to that SDK root. + +Use this skill only when the new E2E tests Java SDK surface area; shared +runtime functionality available entirely through the SDK belongs in the +TypeScript SDK suite (`nodejs/test/e2e/`). ## Overview @@ -27,43 +22,43 @@ The Java E2E tests use a **replay proxy** (`test/harness/replayingCapiProxy.ts`) that intercepts HTTP calls to the Copilot API and returns pre-recorded responses from YAML snapshot files. This avoids needing real authentication in CI. -**Key constraint:** Java's `CapiProxy.java` always sets `GITHUB_ACTIONS=true` -(line 104), which forces the replay proxy into read-only mode. You **cannot** -record snapshots by running Java tests — you must handcraft the YAML. +**Key constraint:** Java's `CapiProxy.start()` always sets `GITHUB_ACTIONS=true`, +which forces the replay proxy into read-only mode: a Java test cannot record a +snapshot, and every request must match an existing one. ## Step-by-Step Workflow ### Step 1: Choose a snapshot category and snapshot base name - Category = a directory under `test/snapshots/` (e.g., `system_message_sections`) -- Snapshot base name = the exact filename stem to use (already lowercase/underscore-separated), - e.g., `should_use_replaced_identity_section_in_response` +- Snapshot base name = the exact filename stem, already lowercase snake_case, + e.g., `should_use_replaced_identity_section_in_response`. Pass it verbatim to + `configureForTest`: it lowercases and replaces non-alphanumerics with `_` but + does not split camelCase, so `myTestMethod` would look for `mytestmethod.yaml`. - Resulting file: `test/snapshots//.yaml` -### Step 2: Create the YAML snapshot file - -The format is: - -```yaml -models: - - claude-sonnet-5 -conversations: - - messages: - - role: system - content: ${system} - - role: user - content: - - role: assistant - content: -``` - -**Rules:** -- `${system}` is a placeholder that matches ANY system message content -- `${workdir}` in tool arguments is substituted with the actual temp workDir -- Each conversation entry represents one request-response exchange -- For multi-turn, add multiple conversation entries -- For tool calls, include `tool_calls` on assistant messages and `role: tool` for results -- The user content must **exactly match** what your test sends (after normalization) +### Step 2: Use a recorded snapshot + +The SDK guide forbids hand-authored model responses (`CONTRIBUTING.md`, +"Recording and replaying SDK tests"). Reuse a capture that another SDK suite +recorded for the same conversation, as most Java ITs do; if none exists, record +one through the TypeScript SDK suite with the same snapshot name and prompt, +following that guide. + +**Replay rules:** +- Every request's system message is replaced with `${system}` before matching, + so system content is never compared, and an assertion on the reply cannot + prove that a system-message setting reached the model. +- User content must **exactly match** the snapshot's (after normalization), so + copy the prompt from the YAML. +- `${workdir}` stands for the test's temp workDir in tool arguments and results. +- A request matches when its messages equal a conversation up to an assistant + message; the proxy replies with that assistant message and any assistant + messages directly after it. +- A request that matches nothing fails. The proxy's stderr, echoed as + `[CapiProxy stderr]`, reports "No cached response found" with the first + mismatching message per conversation, and `configureForTest` logs the prompts + the snapshot expects. ### Step 3: Create the Java IT test class @@ -146,9 +141,11 @@ pnpm run generate:sdk:java pnpm run build:cli ``` -The facade's runtime paths apply only to its child process. For a focused Maven -run, set the same-checkout wrapper path explicitly, then use the `verify` -lifecycle so test-resource setup and `failsafe:verify` both run: +`pnpm run test:sdk:java` runs the whole suite and takes no test filter. For a +focused Maven run, pass the same-checkout wrapper explicitly (without +`copilot.cli.path` the tests use the release pinned by `nodejs/package.json`), +then use the `verify` lifecycle so test-resource setup and `failsafe:verify` +both run: ```sh cd src/sdk/java @@ -157,7 +154,9 @@ COPILOT_CLI_PATH=/dist-cli/prebuilds//copilot-runtime ./mvnw -pl sdk verify -Dit.test="MyFeatureIT#myTestMethod" -Dcopilot.cli.path="$COPILOT_CLI_PATH" ``` -Use `copilot-runtime.exe` for the wrapper name on Windows. +`` is Node's `-` (for example +`linux-x64`, `darwin-arm64`, `win32-x64`); use `copilot-runtime.exe` for the +wrapper name on Windows. In the standalone SDK repository, start at `java/` and use the pinned runtime: @@ -175,77 +174,15 @@ In the standalone SDK repository, start at `java/` and use the pinned runtime: | Replay proxy (TypeScript) | `test/harness/replayingCapiProxy.ts` | | Proxy server entry point | `test/harness/server.ts` | | Snapshot files | `test/snapshots//.yaml` | -| Existing IT tests for reference | `java/sdk/src/test/java/com/github/copilot/*IT.java` | - -## How the Proxy Matches Requests - -1. The proxy normalizes the incoming request's messages -2. It compares against each conversation in the YAML: - - System message matches if YAML has `${system}` (wildcard) - - User messages are compared by content (exact text match) - - Tool results are compared after normalizing `${workdir}` paths -3. If a match is found, the proxy returns the **next assistant message after the matched request prefix** -4. If no match, in CI mode (`GITHUB_ACTIONS=true`) it errors with "No cached response found" - -## YAML Format for Tool Calls - -If your test involves tool use: - -```yaml -conversations: - # First exchange: model wants to call a tool - - messages: - - role: system - content: ${system} - - role: user - content: Read the file test.txt - - role: assistant - content: I'll read that file. - tool_calls: - - id: toolcall_0 - type: function - function: - name: view - arguments: '{"path":"${workdir}/test.txt"}' - # Second exchange: after tool result is provided, model gives final answer - - messages: - - role: system - content: ${system} - - role: user - content: Read the file test.txt - - role: assistant - content: I'll read that file. - tool_calls: - - id: toolcall_0 - type: function - function: - name: view - arguments: '{"path":"${workdir}/test.txt"}' - - role: tool - tool_call_id: toolcall_0 - content: "1. Hello world!" - - role: assistant - content: The file test.txt contains "Hello world!" -``` +| Existing IT tests for reference | `java/sdk/src/test/java/com/github/copilot/**/*IT.java`; `SystemMessageSectionsIT` reuses Node.js suite captures, including a `view` tool call | + +## Tests that call built-in tools -**Important:** When the model calls tools like `view`, the CLI actually executes -them locally. The file must exist in the test's workDir. Create it in your test -before sending the prompt: +When the snapshot has the model call a built-in tool such as `view`, the CLI +really executes it in the test's workDir, and the next request carries its +result, which must match the snapshot's. Create any file the tool reads before +sending the prompt, with exactly the content the snapshot's tool result shows: ```java -Files.writeString(ctx.getWorkDir().resolve("test.txt"), "Hello world!\n"); +Files.writeString(ctx.getWorkDir().resolve("test.txt"), "Hello transform!"); ``` - -## Common Pitfalls - -1. **Prompt mismatch** — The user content in YAML must exactly match what - `session.sendAndWait(new MessageOptions().setPrompt("..."))` sends. -2. **Forgetting `${system}`** — Always use `${system}` for the system role content - unless testing a specific system message matching scenario. -3. **Tool execution** — If the snapshot has the model calling `view` or other - built-in tools, the CLI will actually execute those tools. Files must exist. -4. **Snapshot name parameter** — pass the explicit snapshot base name to - `configureForTest`, e.g., `configureForTest("category", "my_method_name")`. - Do not rely on camelCase-to-snake_case conversion. -5. **Cannot record via Java** — `CapiProxy.java` forces `GITHUB_ACTIONS=true`. - Always handcraft snapshots or use the Node.js proxy directly for recording. diff --git a/.github/skills/sdk-java-e2e-test/examples.md b/.github/skills/sdk-java-e2e-test/examples.md deleted file mode 100644 index 6cd98d1587..0000000000 --- a/.github/skills/sdk-java-e2e-test/examples.md +++ /dev/null @@ -1,181 +0,0 @@ -# Examples: New Java E2E Test with YAML Snapshot - -These examples document existing Java tests. For new E2E tests of shared -runtime behavior available through the SDK, use the TypeScript SDK suite -instead; add Java E2Es only for Java SDK surface area. - -## Example 1: Simple single-turn conversation (no tool calls) - -### Snapshot YAML - -File: `test/snapshots/system_message_sections/should_use_replaced_identity_section_in_response.yaml` - -```yaml -models: - - claude-sonnet-5 -conversations: - - messages: - - role: system - content: ${system} - - role: user - content: Who are you? - - role: assistant - content: >- - I'm Botanica, your helpful gardening assistant! I'm here to help you - with all things related to plants and gardening. Whether you have - questions about plant care, garden design, soil preparation, pest - management, or anything else in the world of gardening, I'm happy to - help. What would you like to know about plants or gardening today? -``` - -### Corresponding Java test method - -```java -@Test -void shouldUseReplacedIdentitySectionInResponse() throws Exception { - ctx.configureForTest("system_message_sections", "should_use_replaced_identity_section_in_response"); - - var systemMessage = new SystemMessageConfig().setMode(SystemMessageMode.CUSTOMIZE) - .setSections(Map.of(SystemMessageSections.IDENTITY, - new SectionOverride().setAction(SectionOverrideAction.REPLACE) - .setContent("You are a helpful gardening assistant called Botanica. " - + "You only answer questions about plants and gardening."))); - - try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client.createSession(new SessionConfig().setSystemMessage(systemMessage) - .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(30, TimeUnit.SECONDS); - - try { - AssistantMessageEvent response = session - .sendAndWait(new MessageOptions().setPrompt("Who are you?"), 60_000).get(90, TimeUnit.SECONDS); - - assertNotNull(response, "Expected a response from the assistant"); - String content = response.getData().content().toLowerCase(); - assertTrue(content.contains("botanica") || content.contains("garden") || content.contains("plant"), - "Expected response to reflect the replaced identity section, but got: " - + response.getData().content()); - } finally { - session.close(); - } - } -} -``` - -**Key points:** -- `configureForTest("system_message_sections", "should_use_replaced_identity_section_in_response")` - maps to `test/snapshots/system_message_sections/should_use_replaced_identity_section_in_response.yaml` -- The prompt `"Who are you?"` exactly matches the YAML's user content -- `ctx.createClient()` uses `fake-token-for-e2e-tests` — works in CI - ---- - -## Example 2: Multi-turn with tool calls (from existing tests) - -### Snapshot YAML - -File: `test/snapshots/system_message_transform/should_invoke_transform_callbacks_with_section_content.yaml` - -```yaml -models: - - claude-sonnet-5 -conversations: - # First exchange: model decides to call tools - - messages: - - role: system - content: ${system} - - role: user - content: Read the contents of test.txt and tell me what it says - - role: assistant - content: I'll read the test.txt file for you. - tool_calls: - - id: toolcall_0 - type: function - function: - name: report_intent - arguments: '{"intent":"Reading test.txt file"}' - - id: toolcall_1 - type: function - function: - name: view - arguments: '{"path":"${workdir}/test.txt"}' - # Second exchange: after tool results come back, model gives final answer - - messages: - - role: system - content: ${system} - - role: user - content: Read the contents of test.txt and tell me what it says - - role: assistant - content: I'll read the test.txt file for you. - tool_calls: - - id: toolcall_0 - type: function - function: - name: report_intent - arguments: '{"intent":"Reading test.txt file"}' - - id: toolcall_1 - type: function - function: - name: view - arguments: '{"path":"${workdir}/test.txt"}' - - role: tool - tool_call_id: toolcall_0 - content: Intent logged - - role: tool - tool_call_id: toolcall_1 - content: 1. Hello transform! - - role: assistant - content: |- - The file test.txt contains: - ``` - Hello transform! - ``` -``` - -### Corresponding Java test method - -```java -@Test -void transformOnIdentitySectionReceivesNonEmptyContent() throws Exception { - ctx.configureForTest("system_message_transform", "should_invoke_transform_callbacks_with_section_content"); - - ConcurrentHashMap capturedContent = new ConcurrentHashMap<>(); - - var systemMessage = new SystemMessageConfig().setMode(SystemMessageMode.CUSTOMIZE) - .setSections(Map.of(SystemMessageSections.IDENTITY, new SectionOverride().setTransform(content -> { - capturedContent.put("identity", content); - return CompletableFuture.completedFuture(content); - }), SystemMessageSections.TONE, new SectionOverride().setTransform(content -> { - capturedContent.put("tone", content); - return CompletableFuture.completedFuture(content); - }))); - - try (CopilotClient client = ctx.createClient()) { - // Create the file the snapshot expects the CLI view tool to read - Path testFile = ctx.getWorkDir().resolve("test.txt"); - Files.writeString(testFile, "Hello transform!"); - - CopilotSession session = client.createSession(new SessionConfig().setSystemMessage(systemMessage) - .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(30, TimeUnit.SECONDS); - - try { - AssistantMessageEvent response = session - .sendAndWait(new MessageOptions() - .setPrompt("Read the contents of test.txt and tell me what it says"), 60_000) - .get(90, TimeUnit.SECONDS); - - assertNotNull(response, "Expected a response from the assistant"); - - String identityContent = capturedContent.get("identity"); - assertNotNull(identityContent, "Expected identity transform callback to be invoked"); - assertTrue(!identityContent.isBlank(), "Expected identity section content to be non-empty"); - } finally { - session.close(); - } - } -} -``` - -**Key points:** -- The file `test.txt` must be created in `ctx.getWorkDir()` **before** sending the prompt -- The CLI's `view` tool will actually read that file; the YAML's tool result `"1. Hello transform!"` must match what `view` returns for that file content -- Two conversation entries: first for the tool-call decision, second for the final response after tool results diff --git a/.github/workflows/java-publish-snapshot.yml b/.github/workflows/java-publish-snapshot.yml index ce5b8c5f4e..77d5287964 100644 --- a/.github/workflows/java-publish-snapshot.yml +++ b/.github/workflows/java-publish-snapshot.yml @@ -171,6 +171,80 @@ jobs: if-no-files-found: error retention-days: 1 + build-linuxmusl-arm64-classifier: + name: Build Linux musl ARM64 snapshot classifier + needs: resolve-source + runs-on: ubuntu-24.04-arm + permissions: + contents: read + outputs: + version: ${{ steps.build.outputs.version }} + defaults: + run: + shell: bash + working-directory: ./java + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + ref: ${{ github.sha }} + fetch-depth: 1 + persist-credentials: false + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: 22 + + - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 + with: + java-version: "25" + distribution: "microsoft" + cache: "maven" + + - name: Build and validate linuxmusl-arm64 classifier + id: build + run: | + set -euo pipefail + SOURCE_COMMIT=$(git rev-parse HEAD) + if [ "$SOURCE_COMMIT" != "${{ needs.resolve-source.outputs.source_sha }}" ]; then + echo "::error::Checked out $SOURCE_COMMIT instead of the resolved snapshot source." + exit 1 + fi + VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) + docker run --rm \ + --volume "$GITHUB_WORKSPACE:/workspace" \ + --volume "$HOME/.m2:/root/.m2" \ + --workdir /workspace/java \ + --env HOST_GID="$(id -g)" \ + --env HOST_UID="$(id -u)" \ + "eclipse-temurin:25-jdk-alpine" \ + sh -c "apk add --no-cache git java-cacerts maven nodejs npm && + export JAVA_TOOL_OPTIONS=-Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts && + git config --global --add safe.directory /workspace && + node copilot-native/scripts/validate-native-host.mjs linuxmusl-arm64 && + mvn -B -pl copilot-native package -DskipTests -Dcopilot.native.libc=musl && + chown -R \$HOST_UID:\$HOST_GID copilot-native/target" + JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-linuxmusl-arm64.jar" + PRIMARY_JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION.jar" + test -f "$JAR" + node copilot-native/scripts/validate-native-artifact.mjs \ + classifier linuxmusl-arm64 "$JAR" "$(basename "$JAR")" .. + node copilot-native/scripts/validate-native-artifact.mjs placeholder "$PRIMARY_JAR" + MANIFEST="copilot-native/target/linuxmusl-arm64-$VERSION.sha256" + HASH=$(sha256sum "$JAR" | cut -d ' ' -f 1) + printf '%s %s' "$HASH" "$(basename "$JAR")" > "$MANIFEST" + node copilot-native/scripts/validate-native-artifact.mjs \ + checksum "$JAR" "$MANIFEST" "$(basename "$JAR")" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: java-native-linuxmusl-arm64-snapshot-${{ github.run_id }}-${{ github.run_attempt }} + path: | + java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-linuxmusl-arm64.jar + java/copilot-native/target/linuxmusl-arm64-${{ steps.build.outputs.version }}.sha256 + if-no-files-found: error + retention-days: 1 + build-windows-x64-classifier: name: Build Windows x64 snapshot classifier needs: resolve-source @@ -426,6 +500,7 @@ jobs: resolve-source, build-linux-arm64-classifier, build-linuxmusl-x64-classifier, + build-linuxmusl-arm64-classifier, build-windows-x64-classifier, build-windows-arm64-classifier, build-darwin-classifier, @@ -468,6 +543,11 @@ jobs: name: java-native-linuxmusl-x64-snapshot-${{ github.run_id }}-${{ github.run_attempt }} path: ${{ runner.temp }}/java-native-linuxmusl-x64 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: java-native-linuxmusl-arm64-snapshot-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ runner.temp }}/java-native-linuxmusl-arm64 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: java-native-win32-x64-snapshot-${{ github.run_id }}-${{ github.run_attempt }} @@ -569,6 +649,31 @@ jobs: echo "linuxmusl_x64_jar=$JAR" >> "$GITHUB_OUTPUT" echo "linuxmusl_x64_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT" + - name: Verify version, source, and Linux musl ARM64 classifier + id: linuxmusl-arm64-artifact + run: | + SOURCE_COMMIT=$(git rev-parse HEAD) + if [ "$SOURCE_COMMIT" != "${{ needs.resolve-source.outputs.source_sha }}" ]; then + echo "::error::Checked out $SOURCE_COMMIT instead of the resolved snapshot source." + exit 1 + fi + VERSION="${{ steps.linux-arm64-artifact.outputs.version }}" + if [ "$VERSION" != "${{ needs.build-linuxmusl-arm64-classifier.outputs.version }}" ]; then + echo "::error::Linux musl ARM64 classifier version does not match deploy version." + exit 1 + fi + ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-linuxmusl-arm64" + JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-linuxmusl-arm64.jar" + MANIFEST="$ARTIFACT_DIRECTORY/linuxmusl-arm64-$VERSION.sha256" + test -f "$JAR" + test -f "$MANIFEST" + node "$GITHUB_WORKSPACE/java/copilot-native/scripts/validate-native-artifact.mjs" \ + checksum "$JAR" "$MANIFEST" "$(basename "$JAR")" + node "$GITHUB_WORKSPACE/java/copilot-native/scripts/validate-native-artifact.mjs" \ + classifier linuxmusl-arm64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE" + echo "linuxmusl_arm64_jar=$JAR" >> "$GITHUB_OUTPUT" + echo "linuxmusl_arm64_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT" + - name: Verify version, source, and Darwin classifier id: darwin-artifact run: | @@ -650,6 +755,7 @@ jobs: mvn -B deploy -DskipTests -DskipITs -Dcopilot.native.libc=glibc \ "-Dcopilot.native.external.linux.arm64.classifier.path=${{ steps.linux-arm64-artifact.outputs.linux_arm64_jar }}" \ "-Dcopilot.native.external.linuxmusl.x64.classifier.path=${{ steps.linuxmusl-x64-artifact.outputs.linuxmusl_x64_jar }}" \ + "-Dcopilot.native.external.linuxmusl.arm64.classifier.path=${{ steps.linuxmusl-arm64-artifact.outputs.linuxmusl_arm64_jar }}" \ "-Dcopilot.native.external.win32.classifier.path=${{ steps.windows-artifact.outputs.windows_jar }}" \ "-Dcopilot.native.external.win32.arm64.classifier.path=${{ steps.windows-arm64-artifact.outputs.windows_arm64_jar }}" \ "-Dcopilot.native.external.darwin.classifier.path=${{ steps.darwin-artifact.outputs.darwin_jar }}" \ @@ -689,6 +795,7 @@ jobs: echo "| \`linux-x64\` | \`ubuntu-latest\` | \`$(basename "$LINUX_JAR")\` | \`$LINUX_SHA\` | Published |" echo "| \`linux-arm64\` | \`ubuntu-24.04-arm\` | \`$(basename "${{ steps.linux-arm64-artifact.outputs.linux_arm64_jar }}")\` | \`${{ steps.linux-arm64-artifact.outputs.linux_arm64_sha }}\` | Published |" echo "| \`linuxmusl-x64\` | \`Alpine x64\` | \`$(basename "${{ steps.linuxmusl-x64-artifact.outputs.linuxmusl_x64_jar }}")\` | \`${{ steps.linuxmusl-x64-artifact.outputs.linuxmusl_x64_sha }}\` | Published |" + echo "| \`linuxmusl-arm64\` | \`Alpine ARM64\` | \`$(basename "${{ steps.linuxmusl-arm64-artifact.outputs.linuxmusl_arm64_jar }}")\` | \`${{ steps.linuxmusl-arm64-artifact.outputs.linuxmusl_arm64_sha }}\` | Published |" echo "| \`win32-x64\` | \`windows-latest\` | \`$(basename "${{ steps.windows-artifact.outputs.windows_jar }}")\` | \`${{ steps.windows-artifact.outputs.windows_sha }}\` | Published |" echo "| \`win32-arm64\` | \`windows-11-arm\` | \`$(basename "${{ steps.windows-arm64-artifact.outputs.windows_arm64_jar }}")\` | \`${{ steps.windows-arm64-artifact.outputs.windows_arm64_sha }}\` | Published |" echo "| \`darwin-x64\` | \`macos-15-intel\` | \`$(basename "${{ steps.darwin-x64-artifact.outputs.darwin_x64_jar }}")\` | \`${{ steps.darwin-x64-artifact.outputs.darwin_x64_sha }}\` | Published |" diff --git a/.github/workflows/java-smoke-test.yml b/.github/workflows/java-smoke-test.yml deleted file mode 100644 index 95f662beb1..0000000000 --- a/.github/workflows/java-smoke-test.yml +++ /dev/null @@ -1,125 +0,0 @@ -name: "Java smoke test" - -on: - workflow_dispatch: - workflow_call: - secrets: - COPILOT_GITHUB_TOKEN: - required: true - -permissions: - contents: read - -jobs: - smoke-test-jdk17: - name: Build SDK and run smoke test (JDK 17) - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' - defaults: - run: - shell: bash - working-directory: ./java - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Set up JDK 17 - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - with: - java-version: "17" - distribution: "microsoft" - cache: "maven" - - - uses: ./.github/actions/setup-copilot - id: setup-copilot - - - name: Build SDK and install to local repo - run: mvn -DskipTests -DskipITs -Pskip-test-harness clean install - - - name: Create and run smoke test via Copilot CLI - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_CLI_JS: ${{ steps.setup-copilot.outputs.javascript-cli-path }} - run: | - cat > /tmp/smoke-test-prompt.txt << 'PROMPT_EOF' - You are running inside the copilot-sdk monorepo, in the java/ subdirectory. - The SDK has already been built and installed into the local Maven repository. - JDK 17 and Maven are already installed and on PATH. - - Execute the prompt at `sdk/src/test/prompts/PROMPT-smoke-test.md` with the following critical overrides: - - **Critical override — disable SNAPSHOT updates (but allow downloads):** The goal of this workflow is to validate the SDK SNAPSHOT that was just built and installed locally, not any newer SNAPSHOT that might exist in a remote repository. To ensure Maven does not download a newer timestamped SNAPSHOT of the SDK while still allowing it to download any missing plugins or dependencies, you must run the smoke-test Maven build without `-U` and with `--no-snapshot-updates`, so that it uses the locally installed SDK artifact. Use `mvn --no-snapshot-updates clean package` instead of `mvn -U clean package` or `mvn -o clean package`. - - **Critical override — do NOT run the jar:** Stop after the `mvn --no-snapshot-updates clean package` build succeeds. Do NOT execute Step 4 (java -jar) or Step 5 (verify exit code) from the prompt. The workflow will run the jar in a separate deterministic step to guarantee the exit code propagates correctly. - - Follow steps 1-3 only: create the `smoke-test/` directory, create `pom.xml` and the Java source file exactly as specified, and build with `mvn --no-snapshot-updates clean package` (no SNAPSHOT updates and without `-U`). - - If any step fails, exit with a non-zero exit code. Do not silently fix errors. - PROMPT_EOF - - node "$COPILOT_CLI_JS" --yolo --prompt "$(cat /tmp/smoke-test-prompt.txt)" - - - name: Run smoke test jar - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - run: | - cd smoke-test - java -jar ./target/copilot-sdk-smoketest-1.0-SNAPSHOT.jar - echo "Smoke test passed (exit code 0)" - - smoke-test-java25: - name: Build SDK and run smoke test (JDK 25) - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' - defaults: - run: - shell: bash - working-directory: ./java - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Set up JDK 25 - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5 - with: - java-version: "25" - distribution: "microsoft" - cache: "maven" - - - uses: ./.github/actions/setup-copilot - id: setup-copilot - - - name: Build SDK and install to local repo - run: mvn -DskipTests -DskipITs -Pskip-test-harness clean install - - - name: Create and run smoke test via Copilot CLI - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - COPILOT_CLI_JS: ${{ steps.setup-copilot.outputs.javascript-cli-path }} - run: | - cat > /tmp/smoke-test-prompt.txt << 'PROMPT_EOF' - You are running inside the copilot-sdk monorepo, in the java/ subdirectory. - The SDK has already been built and installed into the local Maven repository. - JDK 25 and Maven are already installed and on PATH. - - Execute the prompt at `sdk/src/test/prompts/PROMPT-smoke-test.md` with the following critical overrides: - - **Critical override — disable SNAPSHOT updates (but allow downloads):** The goal of this workflow is to validate the SDK SNAPSHOT that was just built and installed locally, not any newer SNAPSHOT that might exist in a remote repository. To ensure Maven does not download a newer timestamped SNAPSHOT of the SDK while still allowing it to download any missing plugins or dependencies, you must run the smoke-test Maven build without `-U` and with `--no-snapshot-updates`, so that it uses the locally installed SDK artifact. Use `mvn --no-snapshot-updates clean package` instead of `mvn -U clean package` or `mvn -o clean package`. - - **Critical override — do NOT run the jar:** Stop after the `mvn --no-snapshot-updates clean package` build succeeds. Do NOT execute Step 4 (java -jar) or Step 5 (verify exit code) from the prompt. The workflow will run the jar in a separate deterministic step to guarantee the exit code propagates correctly. - - **Critical override — enable Virtual Threads for JDK 25:** After creating the Java source file from the README "Quick Start" section but BEFORE building, you must modify the source file to enable virtual thread support. The Quick Start code contains inline comments that start with `// JDK 25+:` — these are instructions. Find every such comment and follow what it says (comment out lines it says to comment out, uncomment lines it says to uncomment). Add any imports required by the newly uncommented code (e.g. `java.util.concurrent.Executors`). - Also set `maven.compiler.source` and `maven.compiler.target` to `25` in the `pom.xml`. - - Follow steps 1-3 only: create the `smoke-test/` directory, create `pom.xml` and the Java source file exactly as specified, apply the JDK 25 virtual thread modifications described above, and build with `mvn --no-snapshot-updates clean package` (no SNAPSHOT updates and without `-U`). - - If any step fails, exit with a non-zero exit code. Do not silently fix errors. - PROMPT_EOF - - node "$COPILOT_CLI_JS" --yolo --prompt "$(cat /tmp/smoke-test-prompt.txt)" - - - name: Run smoke test jar - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - run: | - cd smoke-test - java -jar ./target/copilot-sdk-smoketest-1.0-SNAPSHOT.jar - echo "Smoke test passed (exit code 0)" diff --git a/.github/workflows/sdk-dotnet.yml b/.github/workflows/sdk-dotnet.yml index 4514817827..63b689f94e 100644 --- a/.github/workflows/sdk-dotnet.yml +++ b/.github/workflows/sdk-dotnet.yml @@ -1,6 +1,6 @@ # Invoked by sdk.yml to run the .NET SDK build/test matrix, documentation, -# backend coverage, full subprocess CAPI coverage, and focused in-process smoke -# coverage on Linux and x64 musl. +# full subprocess CAPI coverage, and focused in-process smoke +# coverage across standard platforms and x64 musl. name: "SDK .NET" env: @@ -25,13 +25,9 @@ permissions: jobs: dotnet-linux-x64: - name: ".NET (ubuntu-latest, ${{ matrix.transport }}, CAPI)" + name: ".NET (ubuntu-latest, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'linux-x64' timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - transport: [default, inprocess] runs-on: ubuntu-latest defaults: run: @@ -39,16 +35,15 @@ jobs: working-directory: ${{ inputs.sdk-home }}/dotnet env: COPILOT_SDK_E2E_BACKEND: capi - DOTNET_TEST_FILTER: ${{ matrix.transport == 'inprocess' && 'FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientE2ETests.Should_Start_And_Connect_Over_InProcess_Ffi|FullyQualifiedName~GitHub.Copilot.Test.E2E.SessionE2ETests.Should_Receive_Session_Events|FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientOptionsE2ETests.Should_Use_Configured_GitHub_Host_For_Authentication' || '' }} - COPILOT_SDK_DEFAULT_CONNECTION: ${{ matrix.transport == 'inprocess' && 'inprocess' || '' }} + DOTNET_TEST_RESULTS_DIRECTORY: TestResults/subprocess steps: - uses: actions/checkout@v7 timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-Linux-X64-gnu + 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: . @@ -61,11 +56,10 @@ jobs: - run: dotnet restore -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" -p:CopilotSkipCliDownload=true # TEMPORARY MERGE QUEUE REDUCTION: pull requests and main pushes retain # static checks; merge groups rerun only SDK compatibility coverage. - - if: github.event_name != 'merge_group' && matrix.transport == 'default' + - if: github.event_name != 'merge_group' run: dotnet format --verify-no-changes --no-restore - run: dotnet build --no-restore -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" -p:CopilotSkipCliDownload=true - name: Test structured output with reflection enabled - if: matrix.transport == 'default' env: DOTNET_ROLL_FORWARD: Major run: >- @@ -76,21 +70,38 @@ jobs: --filter "FullyQualifiedName~GitHub.Copilot.Test.Unit.ClientSessionLifetimeTests.StructuredOutput" - run: npm ci --ignore-scripts working-directory: ${{ inputs.sdk-home }}/test/harness - - env: + - name: Test out-of-process + id: subprocess + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} run: ../scripts/ci/run-dotnet-tests.sh + - name: Test in-process smoke + if: ${{ !cancelled() && (steps.subprocess.outcome == 'success' || steps.subprocess.outcome == 'failure') }} + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + COPILOT_SDK_DEFAULT_CONNECTION: inprocess + DOTNET_TEST_RESULTS_DIRECTORY: TestResults/inprocess + DOTNET_TEST_FILTER: "FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientE2ETests.Should_Start_And_Connect_Over_InProcess_Ffi|FullyQualifiedName~GitHub.Copilot.Test.E2E.SessionE2ETests.Should_Receive_Session_Events|FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientOptionsE2ETests.Should_Use_Configured_GitHub_Host_For_Authentication" + run: ../scripts/ci/run-dotnet-tests.sh + - name: Validate documentation examples + if: github.event_name != 'merge_group' + working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation + run: | + npm ci + npm run extract + npm run validate:cs - if: failure() uses: actions/upload-artifact@v7 with: - name: dotnet-test-diagnostics-ubuntu-latest-${{ matrix.transport }}-capi-${{ github.run_attempt }} + name: dotnet-test-diagnostics-ubuntu-latest-capi-${{ github.run_attempt }} path: ${{ inputs.sdk-home }}/dotnet/TestResults/ if-no-files-found: warn retention-days: 7 dotnet-darwin-arm64: - name: ".NET (macos-latest, default, CAPI)" + name: ".NET (macos-26-xlarge, default, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'darwin-arm64' - runs-on: macos-latest + runs-on: macos-26-xlarge timeout-minutes: 30 defaults: run: @@ -98,14 +109,15 @@ jobs: working-directory: ${{ inputs.sdk-home }}/dotnet env: COPILOT_SDK_E2E_BACKEND: capi + DOTNET_TEST_RESULTS_DIRECTORY: TestResults/subprocess steps: - uses: actions/checkout@v7 timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-macOS-ARM64 + artifact-name: executable-sdk-macOS-ARM64 path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore && node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare working-directory: . @@ -119,13 +131,23 @@ jobs: - run: dotnet build --no-restore -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" -p:CopilotSkipCliDownload=true - run: npm ci --ignore-scripts working-directory: ${{ inputs.sdk-home }}/test/harness - - env: + - name: Test out-of-process + id: subprocess + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + run: ../scripts/ci/run-dotnet-tests.sh + - name: Test in-process smoke + if: ${{ !cancelled() && (steps.subprocess.outcome == 'success' || steps.subprocess.outcome == 'failure') }} + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + COPILOT_SDK_DEFAULT_CONNECTION: inprocess + DOTNET_TEST_RESULTS_DIRECTORY: TestResults/inprocess + DOTNET_TEST_FILTER: "FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientE2ETests.Should_Start_And_Connect_Over_InProcess_Ffi|FullyQualifiedName~GitHub.Copilot.Test.E2E.SessionE2ETests.Should_Receive_Session_Events|FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientOptionsE2ETests.Should_Use_Configured_GitHub_Host_For_Authentication" run: ../scripts/ci/run-dotnet-tests.sh - if: failure() uses: actions/upload-artifact@v7 with: - name: dotnet-test-diagnostics-macos-latest-default-capi-${{ github.run_attempt }} + name: dotnet-test-diagnostics-macos-26-xlarge-default-capi-${{ github.run_attempt }} path: ${{ inputs.sdk-home }}/dotnet/TestResults/ if-no-files-found: warn retention-days: 7 @@ -142,14 +164,15 @@ jobs: env: COPILOT_SDK_E2E_BACKEND: capi DOTNET_TEST_RUNTIME: win-x64 + DOTNET_TEST_RESULTS_DIRECTORY: TestResults/subprocess steps: - uses: actions/checkout@v7 timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-Windows-X64 + artifact-name: executable-sdk-Windows-X64 path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore && node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare working-directory: . @@ -164,86 +187,42 @@ jobs: - run: npm ci --ignore-scripts working-directory: ${{ inputs.sdk-home }}/test/harness - run: pwsh.exe -Command "Write-Host 'PowerShell ready'" - - env: + - name: Test out-of-process + id: subprocess + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} run: ../scripts/ci/run-dotnet-tests.sh - - if: failure() - uses: actions/upload-artifact@v7 - with: - name: dotnet-test-diagnostics-windows-latest-default-capi-${{ github.run_attempt }} - path: ${{ inputs.sdk-home }}/dotnet/TestResults/ - if-no-files-found: warn - retention-days: 7 - - # Exercise non-CAPI model backends through .NET only instead of multiplying - # them across every language and execution mode; all SDKs cover both modes with CAPI. - dotnet-backends: - name: ".NET (ubuntu-latest, default, ${{ matrix.backend }})" - if: inputs.platform == 'all' || inputs.platform == 'linux-x64' - runs-on: ubuntu-latest - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - backend: [anthropic-messages, openai-responses, openai-completions] - defaults: - run: - shell: bash - working-directory: ${{ inputs.sdk-home }}/dotnet - env: - COPILOT_SDK_E2E_BACKEND: ${{ matrix.backend }} - DOTNET_TEST_FILTER: "FullyQualifiedName~GitHub.Copilot.Test.E2E&E2EBackend!=SelfConfiguredBackend&E2EBackend!=CapiOnly" - steps: - - uses: actions/checkout@v7 - timeout-minutes: 4 - with: - persist-credentials: false - - uses: actions/download-artifact@v8 - with: - 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-dotnet@v6 - with: - dotnet-version: "10.0.x" - - uses: actions/setup-node@v6 - with: - node-version: 22 - - run: dotnet restore -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" -p:CopilotSkipCliDownload=true - - run: dotnet build --no-restore -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" -p:CopilotSkipCliDownload=true - - run: npm ci --ignore-scripts - working-directory: ${{ inputs.sdk-home }}/test/harness - - env: + - name: Test in-process smoke + if: ${{ !cancelled() && (steps.subprocess.outcome == 'success' || steps.subprocess.outcome == 'failure') }} + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + COPILOT_SDK_DEFAULT_CONNECTION: inprocess + DOTNET_TEST_RESULTS_DIRECTORY: TestResults/inprocess + DOTNET_TEST_FILTER: "FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientE2ETests.Should_Start_And_Connect_Over_InProcess_Ffi|FullyQualifiedName~GitHub.Copilot.Test.E2E.SessionE2ETests.Should_Receive_Session_Events|FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientOptionsE2ETests.Should_Use_Configured_GitHub_Host_For_Authentication" run: ../scripts/ci/run-dotnet-tests.sh - if: failure() uses: actions/upload-artifact@v7 with: - name: dotnet-test-diagnostics-ubuntu-latest-default-${{ matrix.backend }}-${{ github.run_attempt }} + name: dotnet-test-diagnostics-windows-latest-default-capi-${{ github.run_attempt }} path: ${{ inputs.sdk-home }}/dotnet/TestResults/ if-no-files-found: warn retention-days: 7 dotnet-musl-x64: - name: ".NET (Alpine x64, ${{ matrix.transport }}, CAPI)" + name: ".NET (Alpine x64, CAPI)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' runs-on: ubuntu-latest timeout-minutes: 20 env: COPILOT_SDK_E2E_BACKEND: capi - strategy: - fail-fast: false - matrix: - transport: [default, inprocess] steps: - uses: actions/checkout@v7 timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-Linux-X64-musl + artifact-name: executable-sdk-Linux-X64-musl path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore working-directory: . @@ -255,7 +234,6 @@ jobs: image: mcr.microsoft.com/dotnet/sdk:10.0-alpine sdk-root: /workspace/${{ inputs.sdk-home }} workdir: /workspace/${{ inputs.sdk-home }} - transport: ${{ matrix.transport }} command: | dotnet --info | grep -Eq "RID:[[:space:]]+linux-musl-x64" apk add --no-cache nodejs npm @@ -278,50 +256,33 @@ jobs: dotnet restore "$COPILOT_SDK_ROOT/dotnet/test/GitHub.Copilot.SDK.Test.csproj" \ -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" \ -p:CopilotSkipCliDownload=true - set -- - if [ "$COPILOT_SDK_TEST_TRANSPORT" = "inprocess" ]; then - set -- --filter 'FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientE2ETests.Should_Start_And_Connect_Over_InProcess_Ffi|FullyQualifiedName~GitHub.Copilot.Test.E2E.SessionE2ETests.Should_Receive_Session_Events|FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientOptionsE2ETests.Should_Use_Configured_GitHub_Host_For_Authentication' - fi - dotnet test "$COPILOT_SDK_ROOT/dotnet/test/GitHub.Copilot.SDK.Test.csproj" \ - --no-restore \ + dotnet build "$COPILOT_SDK_ROOT/dotnet/test/GitHub.Copilot.SDK.Test.csproj" \ + --no-restore --framework net8.0 \ + -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" \ + -p:CopilotSkipCliDownload=true \ + -p:RunAnalyzers=false + set -- "$COPILOT_SDK_ROOT/dotnet/test/GitHub.Copilot.SDK.Test.csproj" \ + --no-build \ --framework net8.0 \ -v n \ --blame-hang \ --blame-hang-timeout 10m \ --blame-hang-dump-type none \ --logger "trx;LogFilePrefix=test-results" \ - --results-directory "$COPILOT_SDK_ROOT/dotnet/TestResults" \ - "$@" \ -p:CopilotCliBinaryPath="$COPILOT_RUNTIME_BINARY_PATH" \ -p:CopilotSkipCliDownload=true \ -p:RunAnalyzers=false + status=0 + dotnet test "$@" --results-directory "$COPILOT_SDK_ROOT/dotnet/TestResults/subprocess" || status=$? + COPILOT_SDK_DEFAULT_CONNECTION=inprocess dotnet test "$@" \ + --results-directory "$COPILOT_SDK_ROOT/dotnet/TestResults/inprocess" \ + --filter 'FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientE2ETests.Should_Start_And_Connect_Over_InProcess_Ffi|FullyQualifiedName~GitHub.Copilot.Test.E2E.SessionE2ETests.Should_Receive_Session_Events|FullyQualifiedName~GitHub.Copilot.Test.E2E.ClientOptionsE2ETests.Should_Use_Configured_GitHub_Host_For_Authentication' || status=$? + exit "$status" - name: Upload .NET test diagnostics if: failure() uses: actions/upload-artifact@v7 with: - name: dotnet-test-diagnostics-alpine-x64-${{ matrix.transport }}-capi-${{ github.run_attempt }} + name: dotnet-test-diagnostics-alpine-x64-capi-${{ github.run_attempt }} path: ${{ inputs.sdk-home }}/dotnet/TestResults/ if-no-files-found: warn retention-days: 7 - - docs-dotnet: - name: "Docs (.NET)" - if: inputs.platform == 'all' || inputs.platform == 'docs' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - timeout-minutes: 4 - with: - persist-credentials: false - - uses: actions/setup-node@v6 - with: - node-version: 22 - - uses: actions/setup-dotnet@v6 - with: - dotnet-version: "10.0.x" - - run: npm ci - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation - - run: dotnet restore -p:CopilotSkipCliDownload=true - working-directory: ${{ inputs.sdk-home }}/dotnet - - run: npm run extract && npm run validate:cs - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation diff --git a/.github/workflows/sdk-go.yml b/.github/workflows/sdk-go.yml index f613027d39..e7235998f8 100644 --- a/.github/workflows/sdk-go.yml +++ b/.github/workflows/sdk-go.yml @@ -25,13 +25,12 @@ permissions: jobs: go: - name: "Go (${{ matrix.os }}, ${{ matrix.transport }})" + name: "Go (${{ matrix.os }})" if: contains(fromJSON('["all","linux-x64","darwin-arm64","win32-x64"]'), inputs.platform) strategy: fail-fast: false matrix: - os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-latest"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-latest","windows-latest"]') }} - transport: [default, inprocess] + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26","windows-latest"]') }} runs-on: ${{ matrix.os }} defaults: run: @@ -42,9 +41,9 @@ jobs: timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} + artifact-name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore && node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare working-directory: . @@ -56,52 +55,62 @@ jobs: ${{ inputs.sdk-home }}/go/samples/go.sum # TEMPORARY MERGE QUEUE REDUCTION: pull requests and main pushes retain # static checks; merge groups rerun only SDK compatibility coverage. - - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.transport == 'default' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' run: | go fmt ./... git diff --exit-code - - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.transport == 'default' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' uses: golangci/golangci-lint-action@v9 with: working-directory: ${{ inputs.sdk-home }}/go version: latest args: --timeout=5m - - if: runner.os == 'Linux' && matrix.transport == 'default' + - if: runner.os == 'Linux' run: go test ./... working-directory: ${{ inputs.sdk-home }}/go/samples - run: npm ci --ignore-scripts working-directory: ${{ inputs.sdk-home }}/test/harness - if: runner.os == 'Windows' run: pwsh.exe -Command "Write-Host 'PowerShell ready'" - - env: + - name: Test out-of-process + id: subprocess + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + run: /bin/bash test.sh + - name: Test in-process smoke + if: ${{ !cancelled() && (steps.subprocess.outcome == 'success' || steps.subprocess.outcome == 'failure') }} + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} - COPILOT_SDK_DEFAULT_CONNECTION: ${{ matrix.transport == 'inprocess' && 'inprocess' || '' }} - GOFLAGS: ${{ matrix.transport == 'inprocess' && '-tags=copilot_inprocess' || '' }} + COPILOT_SDK_DEFAULT_CONNECTION: inprocess + GOFLAGS: -tags=copilot_inprocess + run: | + go test -v -race -timeout=20m ./internal/e2e -run '^TestInProcessFfiE2E$' + go test -v -race -timeout=20m ./internal/e2e -run '^TestClientE2E$/^should_use_configured_github_host_for_authentication$' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' + uses: actions/setup-node@v6 + with: + node-version: 22 + - name: Validate documentation examples + if: github.event_name != 'merge_group' && runner.os == 'Linux' + working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation run: | - if [ "$COPILOT_SDK_DEFAULT_CONNECTION" = "inprocess" ]; then - go test -v -race -timeout=20m ./internal/e2e -run '^TestInProcessFfiE2E$' - go test -v -race -timeout=20m ./internal/e2e -run '^TestClientE2E$/^should_use_configured_github_host_for_authentication$' - else - /bin/bash test.sh - fi + npm ci + npm run extract + npm run validate:go go-musl-x64: - name: "Go (Alpine x64, ${{ matrix.transport }})" + name: "Go (Alpine x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' runs-on: ubuntu-latest timeout-minutes: 20 - strategy: - fail-fast: false - matrix: - transport: [default, inprocess] steps: - uses: actions/checkout@v7 timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-Linux-X64-musl + artifact-name: executable-sdk-Linux-X64-musl path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore working-directory: . @@ -112,7 +121,6 @@ jobs: image: golang:1.24-alpine sdk-root: /workspace/${{ inputs.sdk-home }} workdir: /workspace/${{ inputs.sdk-home }} - transport: ${{ matrix.transport }} command: | apk add --no-cache build-base nodejs npm npm --prefix "$COPILOT_SDK_ROOT/nodejs" ci --ignore-scripts @@ -130,39 +138,14 @@ jobs: *) echo "Expected the linuxmusl-x64 runtime, got: $COPILOT_CLI_PATH" >&2; exit 1 ;; esac - if [ "$COPILOT_SDK_TEST_TRANSPORT" = "inprocess" ]; then - export GOFLAGS="-tags=copilot_inprocess" - else - unset GOFLAGS - fi export CGO_ENABLED=1 test "$(go env GOARCH)" = "amd64" test "$(go env CGO_ENABLED)" = "1" cd "$COPILOT_SDK_ROOT/go" - if [ "$COPILOT_SDK_TEST_TRANSPORT" = "inprocess" ]; then - go test -v -race -timeout=20m ./internal/e2e -run '^TestInProcessFfiE2E$' - go test -v -race -timeout=20m ./internal/e2e -run '^TestClientE2E$/^should_use_configured_github_host_for_authentication$' - else - /bin/bash test.sh - fi - - docs-go: - name: "Docs (Go)" - if: inputs.platform == 'all' || inputs.platform == 'docs' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - timeout-minutes: 4 - with: - persist-credentials: false - - uses: actions/setup-node@v6 - with: - node-version: 22 - - uses: actions/setup-go@v6 - with: - go-version: "1.24" - cache-dependency-path: ${{ inputs.sdk-home }}/go/go.sum - - run: npm ci - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation - - run: npm run extract && npm run validate:go - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation + status=0 + /bin/bash test.sh || status=$? + export COPILOT_SDK_DEFAULT_CONNECTION=inprocess + export GOFLAGS="-tags=copilot_inprocess" + go test -v -race -timeout=20m ./internal/e2e -run '^TestInProcessFfiE2E$' || status=$? + go test -v -race -timeout=20m ./internal/e2e -run '^TestClientE2E$/^should_use_configured_github_host_for_authentication$' || status=$? + exit "$status" diff --git a/.github/workflows/sdk-java.yml b/.github/workflows/sdk-java.yml index a69caa71d3..fbadf275ce 100644 --- a/.github/workflows/sdk-java.yml +++ b/.github/workflows/sdk-java.yml @@ -1,5 +1,5 @@ -# Invoked by sdk.yml to run full subprocess Java SDK coverage on supported JDKs, -# focused in-process smoke coverage across native classifiers, and documentation. +# Invoked by sdk.yml to run full subprocess Java SDK coverage followed by focused +# in-process smoke on standard platforms and x64 musl, plus JDK 17 and docs checks. name: "SDK Java" env: @@ -24,13 +24,19 @@ permissions: jobs: java: - name: "Java (JDK ${{ matrix.test-jdk }})" - if: inputs.platform == 'all' || inputs.platform == 'linux-x64' + name: "Java (${{ matrix.os }}, JDK ${{ matrix.test-jdk }})" + if: contains(fromJSON('["all","linux-x64","darwin-arm64","win32-x64"]'), inputs.platform) strategy: fail-fast: false matrix: + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26","windows-latest"]') }} test-jdk: ["25", "17"] - runs-on: ubuntu-latest + exclude: + - os: macos-26 + test-jdk: "17" + - os: windows-latest + test-jdk: "17" + runs-on: ${{ matrix.os }} defaults: run: shell: bash @@ -40,9 +46,9 @@ jobs: timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-Linux-X64-gnu + artifact-name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore && node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare working-directory: . @@ -56,14 +62,17 @@ jobs: node-version: 22 # TEMPORARY MERGE QUEUE REDUCTION: pull requests and main pushes retain # static checks; merge groups rerun only SDK compatibility coverage. - - if: github.event_name != 'merge_group' && matrix.test-jdk == '25' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.test-jdk == '25' run: ./scripts/test-update-documentation-versions.sh - run: ./mvnw test-compile jar:jar - - if: github.event_name != 'merge_group' && matrix.test-jdk == '25' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.test-jdk == '25' run: ./mvnw javadoc:javadoc - - if: github.event_name != 'merge_group' && matrix.test-jdk == '25' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.test-jdk == '25' run: ./mvnw spotless:check + - if: runner.os == 'Windows' + run: pwsh.exe -Command "Write-Host 'PowerShell ready'" - if: matrix.test-jdk == '25' + id: subprocess env: CI: "true" run: ./mvnw -pl sdk verify -Dskip.test.harness=true -Dcopilot.cli.path="$COPILOT_CLI_PATH" @@ -76,144 +85,170 @@ jobs: env: CI: "true" run: ./mvnw -pl sdk jacoco:prepare-agent@wire-up-coverage-instrumentation antrun:run@print-test-jdk-banner surefire:test failsafe:integration-test failsafe:verify jacoco:report@build-coverage-report-from-tests -Denforcer.skip=true -Dcopilot.cli.path="$COPILOT_CLI_PATH" + - name: Test in-process smoke + if: ${{ !cancelled() && matrix.test-jdk == '25' && (steps.subprocess.outcome == 'success' || steps.subprocess.outcome == 'failure') }} + env: + CI: "true" + run: | + mkdir -p sdk/target/subprocess-reports + for reports in surefire-reports surefire-reports-isolated failsafe-reports; do + if [ -d "sdk/target/$reports" ]; then + mv "sdk/target/$reports" sdk/target/subprocess-reports/ + fi + done + ./mvnw verify -Pinprocess \ + -Dskip.test.harness=true \ + -Dtest=NoTestsForInProcessSmoke \ + -Dsurefire.failIfNoSpecifiedTests=false \ + -Dit.test=InProcessTransportIT,AuthHostE2ETest \ + -Dcopilot.inprocess.cli.path="$COPILOT_CLI_PATH" + - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.test-jdk == '25' + run: ./mvnw install -Dmaven.test.skip=true -Dskip.test.harness=true -Dcopilot.native.skip.download=true + - name: Validate documentation examples + if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.test-jdk == '25' + working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation + run: | + npm ci + npm run extract + npm run validate:java - if: failure() uses: actions/upload-artifact@v7 with: - name: java-test-results-jdk-${{ matrix.test-jdk }} + name: java-test-results-${{ matrix.os }}-jdk-${{ matrix.test-jdk }} path: | ${{ inputs.sdk-home }}/java/sdk/target/surefire-reports/ ${{ inputs.sdk-home }}/java/sdk/target/surefire-reports-isolated/ ${{ inputs.sdk-home }}/java/sdk/target/failsafe-reports/ + ${{ inputs.sdk-home }}/java/sdk/target/subprocess-reports/ retention-days: 7 - java-inprocess: - name: "Java (inprocess, ${{ matrix.classifier }})" - if: contains(fromJSON('["all","linux-x64","darwin-arm64","win32-x64"]'), inputs.platform) - strategy: - fail-fast: false - matrix: - include: ${{ fromJSON(inputs.platform == 'linux-x64' && '[{"os":"ubuntu-latest","classifier":"linux-x64"}]' || inputs.platform == 'darwin-arm64' && '[{"os":"macos-26","classifier":"darwin-arm64"}]' || inputs.platform == 'win32-x64' && '[{"os":"windows-latest","classifier":"win32-x64"}]' || '[{"os":"ubuntu-latest","classifier":"linux-x64"},{"os":"windows-latest","classifier":"win32-x64"},{"os":"macos-26","classifier":"darwin-arm64"}]') }} - runs-on: ${{ matrix.os }} - defaults: - run: - shell: bash - working-directory: ${{ inputs.sdk-home }}/java + java-musl-x64: + name: "Java (JDK 25, linuxmusl-x64)" + if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' + runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v7 timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} + artifact-name: executable-sdk-Linux-X64-musl path: ${{ github.workspace }} - - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore && node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare + - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore working-directory: . - - uses: actions/setup-java@v5 - with: - java-version: "25" - distribution: microsoft - cache: maven - - uses: actions/setup-node@v6 + - uses: ./.github/actions/run-alpine-tests + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} with: - node-version: 22 - - env: - CI: "true" - MAVEN_ARGS: ${{ matrix.maven-args }} - run: >- - ./mvnw clean verify -Pinprocess - -Dtest=NoTestsForInProcessSmoke - -Dsurefire.failIfNoSpecifiedTests=false - -Dit.test=InProcessTransportIT,AuthHostE2ETest - -Dcopilot.inprocess.cli.path="$COPILOT_CLI_PATH" - $MAVEN_ARGS + image: eclipse-temurin:25-jdk-alpine + sdk-root: /workspace/${{ inputs.sdk-home }} + workdir: /workspace/${{ inputs.sdk-home }}/java + command: | + apk add --no-cache java-cacerts maven nodejs npm + export JAVA_TOOL_OPTIONS=-Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts + export GITHUB_ENV=/tmp/copilot-sdk-runtime.env + export COPILOT_RUNTIME_TARGET=linuxmusl-x64 + export COPILOT_RUNTIME_OUTPUT_DIRECTORY=/workspace/.sdk-runtime/linuxmusl-x64 + node "$COPILOT_SDK_ROOT/scripts/ci/runtime-artifact.mjs" prepare + set -a + . "$GITHUB_ENV" + set +a + node copilot-native/scripts/validate-native-host.mjs linuxmusl-x64 + ./mvnw test-compile jar:jar -Dcopilot.native.libc=musl + status=0 + ./mvnw -pl sdk verify -Dskip.test.harness=true -Dcopilot.cli.path="$COPILOT_CLI_PATH" || status=$? + mkdir -p sdk/target/subprocess-reports + for reports in surefire-reports surefire-reports-isolated failsafe-reports; do + if [ -d "sdk/target/$reports" ]; then + mv "sdk/target/$reports" sdk/target/subprocess-reports/ + fi + done + ./mvnw verify -Pinprocess \ + -Dskip.test.harness=true \ + -Dtest=NoTestsForInProcessSmoke \ + -Dsurefire.failIfNoSpecifiedTests=false \ + -Dit.test=InProcessTransportIT,AuthHostE2ETest \ + -Dcopilot.native.libc=musl \ + -Dcopilot.inprocess.cli.path="$COPILOT_CLI_PATH" || status=$? + exit "$status" - if: failure() uses: actions/upload-artifact@v7 with: - name: java-test-results-inprocess-${{ matrix.classifier }} + name: java-test-results-linuxmusl-x64 path: | ${{ inputs.sdk-home }}/java/sdk/target/surefire-reports/ ${{ inputs.sdk-home }}/java/sdk/target/surefire-reports-isolated/ ${{ inputs.sdk-home }}/java/sdk/target/failsafe-reports/ + ${{ inputs.sdk-home }}/java/sdk/target/subprocess-reports/ retention-days: 7 - - java-inprocess-musl-x64: - name: "Java (inprocess, linuxmusl-x64)" - if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' - runs-on: ubuntu-latest + java-musl-arm64: + 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: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-Linux-X64-musl + artifact-name: executable-sdk-Linux-ARM64-musl path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore working-directory: . - - name: Run Java SDK tests (InProcess, musl) + - name: Run Java SDK tests (linuxmusl-arm64) env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} run: | - docker run --rm \ + docker run --rm --init \ --volume "$GITHUB_WORKSPACE:/workspace" \ --workdir "/workspace/$SDK_HOME/java" \ --env CI=true \ --env COPILOT_HMAC_KEY \ --env GITHUB_ACTIONS=true \ --env GITHUB_WORKSPACE=/workspace \ - --env SDK_HOME="/workspace/$SDK_HOME" \ + --env COPILOT_SDK_ROOT="/workspace/$SDK_HOME" \ eclipse-temurin:25-jdk-alpine \ sh -c 'set -eux - apk add --no-cache git java-cacerts maven nodejs npm - export JAVA_TOOL_OPTIONS=-Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts + apk add --no-cache bash git java-cacerts maven nodejs npm + bash --noprofile --norc -c "exit 0" git config --global --add safe.directory /workspace + export JAVA_TOOL_OPTIONS=-Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts export GITHUB_ENV=/tmp/copilot-sdk-runtime.env - export COPILOT_RUNTIME_TARGET=linuxmusl-x64 - export COPILOT_RUNTIME_OUTPUT_DIRECTORY=/workspace/.sdk-runtime/linuxmusl-x64 - node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare + export COPILOT_RUNTIME_TARGET=linuxmusl-arm64 + export COPILOT_RUNTIME_OUTPUT_DIRECTORY=/workspace/.sdk-runtime/linuxmusl-arm64 + node "$COPILOT_SDK_ROOT/scripts/ci/runtime-artifact.mjs" prepare set -a . "$GITHUB_ENV" set +a - node copilot-native/scripts/validate-native-host.mjs linuxmusl-x64 - ./mvnw clean verify -Pinprocess \ + node copilot-native/scripts/validate-native-host.mjs linuxmusl-arm64 + ./mvnw test-compile jar:jar -Dcopilot.native.libc=musl + status=0 + ./mvnw -pl sdk verify -Dskip.test.harness=true -Dcopilot.cli.path="$COPILOT_CLI_PATH" || status=$? + mkdir -p sdk/target/subprocess-reports + for reports in surefire-reports surefire-reports-isolated failsafe-reports; do + if [ -d "sdk/target/$reports" ]; then + mv "sdk/target/$reports" sdk/target/subprocess-reports/ + fi + done + ./mvnw verify -Pinprocess \ + -Dskip.test.harness=true \ -Dtest=NoTestsForInProcessSmoke \ -Dsurefire.failIfNoSpecifiedTests=false \ -Dit.test=InProcessTransportIT,AuthHostE2ETest \ -Dcopilot.native.libc=musl \ - -Dcopilot.inprocess.cli.path="$COPILOT_CLI_PATH"' + -Dcopilot.inprocess.cli.path="$COPILOT_CLI_PATH" || status=$? + exit "$status"' - if: failure() uses: actions/upload-artifact@v7 with: - name: java-test-results-inprocess-linuxmusl-x64 + name: java-test-results-linuxmusl-arm64 path: | ${{ inputs.sdk-home }}/java/sdk/target/surefire-reports/ ${{ inputs.sdk-home }}/java/sdk/target/surefire-reports-isolated/ ${{ inputs.sdk-home }}/java/sdk/target/failsafe-reports/ + ${{ inputs.sdk-home }}/java/sdk/target/subprocess-reports/ retention-days: 7 - - docs-java: - name: "Docs (Java)" - if: inputs.platform == 'all' || inputs.platform == 'docs' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - timeout-minutes: 4 - with: - persist-credentials: false - - uses: actions/setup-node@v6 - with: - node-version: 22 - - uses: actions/setup-java@v5 - with: - distribution: microsoft - java-version: "25" - cache: maven - - run: ./mvnw install -Dmaven.test.skip=true -Dskip.test.harness=true -Dcopilot.native.skip.download=true - working-directory: ${{ inputs.sdk-home }}/java - - run: npm ci - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation - - run: npm run extract && npm run validate:java - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation diff --git a/.github/workflows/sdk-nodejs.yml b/.github/workflows/sdk-nodejs.yml index 8ea69933cd..9fbbf97e76 100644 --- a/.github/workflows/sdk-nodejs.yml +++ b/.github/workflows/sdk-nodejs.yml @@ -19,19 +19,23 @@ on: sdk-home: required: true type: string + outputs: + capi-result: + description: "Standard-platform CAPI job result, independent of BYOK jobs" + # Direct result outputs can be empty: https://github.com/actions/runner/issues/2495 + value: ${{ fromJSON(toJSON(jobs.nodejs)).result }} permissions: contents: read jobs: nodejs: - name: "Node.js (${{ matrix.os }}, ${{ matrix.transport }})" + name: "Node.js (${{ matrix.os }}, CAPI)" if: contains(fromJSON('["all","linux-x64","darwin-arm64","win32-x64"]'), inputs.platform) strategy: fail-fast: false matrix: - os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-latest"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-latest","windows-latest"]') }} - transport: [default, inprocess] + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26","windows-latest"]') }} runs-on: ${{ matrix.os }} defaults: run: @@ -42,9 +46,9 @@ jobs: timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} + artifact-name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore && node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare working-directory: . @@ -54,16 +58,19 @@ jobs: cache: npm cache-dependency-path: ${{ inputs.sdk-home }}/nodejs/package-lock.json - run: npm ci --ignore-scripts + - name: Install generator test dependencies + run: npm ci --ignore-scripts + working-directory: ${{ inputs.sdk-home }}/scripts/codegen # TEMPORARY MERGE QUEUE REDUCTION: pull requests and main pushes retain # static checks; merge groups rerun only SDK compatibility coverage. - - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.transport == 'default' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' run: npm run format:check - - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.transport == 'default' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' run: npm run lint - - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.transport == 'default' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' run: npm run typecheck - run: npm run build - - if: runner.os == 'Linux' && matrix.transport == 'default' + - if: runner.os == 'Linux' env: COPILOT_SDK_RUNTIME_PLATFORMS: linux-x64 run: | @@ -71,43 +78,104 @@ jobs: npm run verify:release-packages - run: npm ci --ignore-scripts working-directory: ${{ inputs.sdk-home }}/test/harness - - if: runner.os == 'Linux' && matrix.transport == 'default' + - if: runner.os == 'Linux' run: npm test working-directory: ${{ inputs.sdk-home }}/test/harness - - if: runner.os == 'Linux' && matrix.transport == 'default' + - if: runner.os == 'Linux' run: npm ci working-directory: ${{ inputs.sdk-home }}/scripts/corrections - - if: runner.os == 'Linux' && matrix.transport == 'default' + - if: runner.os == 'Linux' run: npm test working-directory: ${{ inputs.sdk-home }}/scripts/corrections - if: runner.os == 'Windows' run: pwsh.exe -Command "Write-Host 'PowerShell ready'" - - env: + - name: Test out-of-process + id: subprocess + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} - COPILOT_SDK_DEFAULT_CONNECTION: ${{ matrix.transport == 'inprocess' && 'inprocess' || '' }} run: | - if [ "$COPILOT_SDK_DEFAULT_CONNECTION" = "inprocess" ]; then - npm test -- test/e2e/inprocess_ffi.e2e.test.ts test/e2e/auth_host.e2e.test.ts + if [ "$GITHUB_EVENT_NAME" = "merge_group" ] || [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then + npm test -- --reporter=default --reporter=json --outputFile="$RUNNER_TEMP/sdk-nodejs-results.json" else npm test fi + - name: Upload Flake Finder Node.js test results + if: always() && (github.event_name == 'merge_group' || github.event_name == 'pull_request') && runner.os == 'Linux' + continue-on-error: true + uses: actions/upload-artifact@v7 + with: + name: suspected-flakes-sdk-nodejs-${{ github.run_attempt }}-linux + path: ${{ runner.temp }}/sdk-nodejs-results.json + if-no-files-found: warn + retention-days: 7 + - name: Test in-process smoke + if: ${{ !cancelled() && github.event_name != 'merge_group' && (steps.subprocess.outcome == 'success' || steps.subprocess.outcome == 'failure') }} + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + COPILOT_SDK_DEFAULT_CONNECTION: inprocess + run: npm test -- test/e2e/inprocess_ffi.e2e.test.ts test/e2e/auth_host.e2e.test.ts + - name: Validate documentation examples + if: github.event_name != 'merge_group' && runner.os == 'Linux' + working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation + run: | + npm ci + npm run extract + npm run validate:ts - nodejs-musl-x64: - name: "Node.js (Alpine x64, ${{ matrix.transport }})" - if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' + # 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 }})" + if: github.event_name != 'merge_group' && (inputs.platform == 'all' || inputs.platform == 'linux-x64') runs-on: ubuntu-latest + timeout-minutes: 30 strategy: fail-fast: false matrix: - transport: [default, inprocess] + backend: [anthropic-messages, openai-responses, openai-completions] + defaults: + run: + shell: bash + working-directory: ${{ inputs.sdk-home }}/nodejs + env: + COPILOT_SDK_E2E_BACKEND: ${{ matrix.backend }} + steps: + - uses: actions/checkout@v7 + timeout-minutes: 4 + 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 out-of-process E2Es + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + run: npm test -- test/e2e + + nodejs-musl-x64: + name: "Node.js (Alpine x64, CAPI)" + if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' + runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-Linux-X64-musl + artifact-name: executable-sdk-Linux-X64-musl path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore working-directory: . @@ -118,9 +186,9 @@ jobs: image: node:22-alpine sdk-root: /workspace/${{ inputs.sdk-home }} workdir: /workspace/${{ inputs.sdk-home }}/nodejs - transport: ${{ matrix.transport }} command: | npm ci --ignore-scripts + (cd "$COPILOT_SDK_ROOT/scripts/codegen" && npm ci --ignore-scripts) npm run build (cd "$COPILOT_SDK_ROOT/test/harness" && npm ci --ignore-scripts) @@ -137,29 +205,7 @@ jobs: esac test -x "$COPILOT_CLI_PATH" test -f "$(dirname "$COPILOT_CLI_PATH")/runtime.node" - if [ "$COPILOT_SDK_TEST_TRANSPORT" = "inprocess" ]; then - npm test -- test/e2e/inprocess_ffi.e2e.test.ts test/e2e/auth_host.e2e.test.ts - else - npm test - fi - - docs-typescript: - name: "Docs (TypeScript)" - if: inputs.platform == 'all' || inputs.platform == 'docs' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - timeout-minutes: 4 - with: - persist-credentials: false - - 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 - working-directory: ${{ inputs.sdk-home }}/nodejs - - run: npm ci - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation - - run: npm run extract && npm run validate:ts - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation + status=0 + npm test || status=$? + COPILOT_SDK_DEFAULT_CONNECTION=inprocess npm test -- test/e2e/inprocess_ffi.e2e.test.ts test/e2e/auth_host.e2e.test.ts || status=$? + exit "$status" diff --git a/.github/workflows/sdk-platform.yml b/.github/workflows/sdk-platform.yml index 2fa62c3b1e..f1ed4e6ed4 100644 --- a/.github/workflows/sdk-platform.yml +++ b/.github/workflows/sdk-platform.yml @@ -1,5 +1,5 @@ -# Runs all SDK language checks for one runtime platform, allowing each platform -# to start as soon as its runtime artifact is available. +# Runs SDK language checks for one runtime platform as soon as its artifact is +# available. Merge groups retain only the Node.js compatibility lane. name: "SDK platform" on: @@ -11,6 +11,14 @@ on: sdk-home: required: true type: string + outputs: + nodejs-result: + description: "Node.js SDK result, independent of other languages" + # Direct result outputs can be empty: https://github.com/actions/runner/issues/2495 + value: ${{ fromJSON(toJSON(jobs.nodejs)).result }} + nodejs-capi-result: + description: "Standard-platform Node.js CAPI result, independent of BYOK and other languages" + value: ${{ jobs.nodejs.outputs.capi-result }} permissions: contents: read @@ -24,6 +32,7 @@ jobs: secrets: inherit python: + if: github.event_name != 'merge_group' uses: ./.github/workflows/sdk-python.yml with: platform: ${{ inputs.platform }} @@ -31,6 +40,7 @@ jobs: secrets: inherit go: + if: github.event_name != 'merge_group' uses: ./.github/workflows/sdk-go.yml with: platform: ${{ inputs.platform }} @@ -38,6 +48,7 @@ jobs: secrets: inherit dotnet: + if: github.event_name != 'merge_group' uses: ./.github/workflows/sdk-dotnet.yml with: platform: ${{ inputs.platform }} @@ -45,6 +56,7 @@ jobs: secrets: inherit rust: + if: github.event_name != 'merge_group' uses: ./.github/workflows/sdk-rust.yml with: platform: ${{ inputs.platform }} @@ -52,6 +64,7 @@ jobs: secrets: inherit java: + if: github.event_name != 'merge_group' uses: ./.github/workflows/sdk-java.yml with: platform: ${{ inputs.platform }} diff --git a/.github/workflows/sdk-python.yml b/.github/workflows/sdk-python.yml index fa56ff533e..2abeca3d8a 100644 --- a/.github/workflows/sdk-python.yml +++ b/.github/workflows/sdk-python.yml @@ -25,13 +25,12 @@ permissions: jobs: python: - name: "Python (${{ matrix.os }}, ${{ matrix.transport }})" + name: "Python (${{ matrix.os }})" if: contains(fromJSON('["all","linux-x64","darwin-arm64","win32-x64"]'), inputs.platform) strategy: fail-fast: false matrix: - os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-latest"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-latest","windows-latest"]') }} - transport: [default, inprocess] + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26","windows-latest"]') }} runs-on: ${{ matrix.os }} timeout-minutes: 20 defaults: @@ -45,9 +44,9 @@ jobs: timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} + artifact-name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore && node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare working-directory: . @@ -63,52 +62,58 @@ jobs: - run: uv sync --locked --all-extras --dev # TEMPORARY MERGE QUEUE REDUCTION: pull requests and main pushes retain # static checks; merge groups rerun only SDK compatibility coverage. - - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.transport == 'default' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' run: uv run --locked ruff format --check . - - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.transport == 'default' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' run: uv run --locked ruff check - - if: github.event_name != 'merge_group' && runner.os == 'Linux' && matrix.transport == 'default' + - if: github.event_name != 'merge_group' && runner.os == 'Linux' run: uv run --locked ty check copilot - run: npm ci --ignore-scripts working-directory: ${{ inputs.sdk-home }}/test/harness - if: runner.os == 'Windows' run: pwsh.exe -Command "Write-Host 'PowerShell ready'" - - env: + - name: Test out-of-process + id: subprocess + env: + COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + run: env -u COPILOT_SKIP_CLI_DOWNLOAD uv run --locked pytest -v -s -n 2 --dist=loadfile + - name: Test in-process smoke + if: ${{ !cancelled() && (steps.subprocess.outcome == 'success' || steps.subprocess.outcome == 'failure') }} + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} - COPILOT_SDK_DEFAULT_CONNECTION: ${{ matrix.transport == 'inprocess' && 'inprocess' || '' }} + COPILOT_SDK_DEFAULT_CONNECTION: inprocess + run: uv run --locked pytest -v -s e2e/test_inprocess_ffi_e2e.py e2e/test_auth_host_e2e.py + - name: Validate documentation examples + if: github.event_name != 'merge_group' && runner.os == 'Linux' + working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation run: | - if [ "$COPILOT_SDK_DEFAULT_CONNECTION" = "inprocess" ]; then - uv run --locked pytest -v -s e2e/test_inprocess_ffi_e2e.py e2e/test_auth_host_e2e.py - else - env -u COPILOT_SKIP_CLI_DOWNLOAD uv run --locked pytest -v -s -n 2 --dist=loadfile - fi + npm ci + npm run extract + uv run --locked --python 3.12 --project "$GITHUB_WORKSPACE/$SDK_HOME/python" python3 -m mypy --version + uv run --locked --python 3.12 --project "$GITHUB_WORKSPACE/$SDK_HOME/python" npm run validate:py - name: Upload Python test diagnostics if: failure() uses: actions/upload-artifact@v7 with: - name: python-test-diagnostics-${{ matrix.os }}-${{ matrix.transport }}-${{ github.run_attempt }} + name: python-test-diagnostics-${{ matrix.os }}-${{ github.run_attempt }} path: ${{ inputs.sdk-home }}/python/.pytest-diagnostics/ include-hidden-files: true if-no-files-found: warn retention-days: 7 python-musl-x64: - name: "Python (Alpine x64, ${{ matrix.transport }})" + name: "Python (Alpine x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' runs-on: ubuntu-latest timeout-minutes: 20 - strategy: - fail-fast: false - matrix: - transport: [default, inprocess] steps: - uses: actions/checkout@v7 timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-Linux-X64-musl + artifact-name: executable-sdk-Linux-X64-musl path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore working-directory: . @@ -119,10 +124,9 @@ jobs: image: python:3.11-alpine sdk-root: /workspace/${{ inputs.sdk-home }} workdir: /workspace/${{ inputs.sdk-home }} - transport: ${{ matrix.transport }} command: | apk add --no-cache nodejs npm - python -m pip install --no-cache-dir uv + python -m pip install --no-cache-dir --timeout 120 uv npm --prefix "$COPILOT_SDK_ROOT/nodejs" ci --ignore-scripts npm --prefix "$COPILOT_SDK_ROOT/test/harness" ci --ignore-scripts @@ -140,43 +144,16 @@ jobs: cd "$COPILOT_SDK_ROOT/python" uv sync --locked --all-extras --dev - if [ "$COPILOT_SDK_TEST_TRANSPORT" = "inprocess" ]; then - uv run --locked pytest -v -s e2e/test_inprocess_ffi_e2e.py e2e/test_auth_host_e2e.py - else - env -u COPILOT_SKIP_CLI_DOWNLOAD uv run --locked pytest -v -s -n 2 --dist=loadfile - fi + status=0 + env -u COPILOT_SKIP_CLI_DOWNLOAD uv run --locked pytest -v -s -n 2 --dist=loadfile || status=$? + COPILOT_SDK_DEFAULT_CONNECTION=inprocess uv run --locked pytest -v -s e2e/test_inprocess_ffi_e2e.py e2e/test_auth_host_e2e.py || status=$? + exit "$status" - name: Upload Python test diagnostics if: failure() uses: actions/upload-artifact@v7 with: - name: python-test-diagnostics-alpine-x64-${{ matrix.transport }}-${{ github.run_attempt }} + name: python-test-diagnostics-alpine-x64-${{ github.run_attempt }} path: ${{ inputs.sdk-home }}/python/.pytest-diagnostics/ include-hidden-files: true if-no-files-found: warn retention-days: 7 - - docs-python: - name: "Docs (Python)" - if: inputs.platform == 'all' || inputs.platform == 'docs' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - timeout-minutes: 4 - with: - persist-credentials: false - - uses: actions/setup-node@v6 - with: - node-version: 22 - - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - uses: astral-sh/setup-uv@v7 - - run: uv sync --locked - working-directory: ${{ inputs.sdk-home }}/python - - run: npm ci - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation - - run: npm run extract - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation - - run: uv run --locked --project "$GITHUB_WORKSPACE/$SDK_HOME/python" python3 -m mypy --version - - run: uv run --locked --project "$GITHUB_WORKSPACE/$SDK_HOME/python" npm run validate:py - working-directory: ${{ inputs.sdk-home }}/scripts/docs-validation diff --git a/.github/workflows/sdk-runtime-artifact.yml b/.github/workflows/sdk-runtime-artifact.yml index e1fa0cb929..0481e945ae 100644 --- a/.github/workflows/sdk-runtime-artifact.yml +++ b/.github/workflows/sdk-runtime-artifact.yml @@ -3,6 +3,9 @@ name: "SDK runtime artifact" env: COPILOT_AUTO_UPDATE: "false" COPILOT_BAZEL_ANG_ENABLED: ${{ vars.COPILOT_BAZEL_ANG_ENABLED }} + COPILOT_BAZEL_ANG_SCOPE: ${{ vars.COPILOT_BAZEL_ANG_SCOPE }} + COPILOT_BAZEL_ANG_PR_SAMPLE_PERCENTAGE: ${{ vars.COPILOT_BAZEL_ANG_PR_SAMPLE_PERCENTAGE }} + COPILOT_BAZEL_ANG_PR_SAMPLED_MODE: ${{ vars.COPILOT_BAZEL_ANG_PR_SAMPLED_MODE }} COPILOT_RUNTIME_E2E_TEST_HOOKS: "1" CARGO_PROFILE_DEV_DEBUG: line-tables-only GIT_HTTP_LOW_SPEED_LIMIT: 1000 @@ -52,13 +55,96 @@ on: permissions: contents: read +concurrency: + group: ${{ inputs.shared-cli-build-compatible && format('project-build-sea-{0}-release-{1}-{2}', inputs.os, inputs.bazel-config, github.sha) || format('sdk-runtime-artifact-{0}-{1}', inputs.target, github.sha) }} + queue: max + jobs: + cache-relay: + if: inputs.runtime-source == 'checkout' + runs-on: ubuntu-slim + timeout-minutes: 15 + permissions: + actions: read + contents: read + outputs: + cache-hit: ${{ steps.cache.outputs.cache-hit }} + steps: + - uses: actions/checkout@v7 + timeout-minutes: 4 + with: + persist-credentials: false + + - uses: actions/setup-node@v6 + with: + node-version-file: .nvmrc + + - name: Capture verified addon source identity + run: node --experimental-strip-types script/resolve-cli-build-identity.ts --github-env + + - name: Restore completed runtime build + id: cache + uses: actions/cache/restore@v6 + with: + path: | + project-build-receipt.txt + dist-cli/ + dist-tmp/release-downloads/ripgrep/ + dist-tmp/release-downloads/tgrep/ + dist-bin/ + dist-pkg-tarballs/ + src/core/sharedApi/runtime-generated/index.d.ts + src/native/runtime/runtime.*.node.metadata + src/native/runtime/copilot-runtime.*.profile + src/native/runtime/index.d.ts + src/native/cli/index.d.ts + !dist-cli/sea.blob + !dist-cli/github-copilot-*.tgz + key: ${{ inputs.shared-cli-build-compatible && format('project-build-v4-sea-{0}-{1}-release-{2}-{3}-{4}', inputs.os, inputs.target, inputs.bazel-config, inputs.use-bazel-addons-on-darwin, github.sha) || format('sdk-project-build-v4-{0}-{1}-{2}-release-{3}-{4}-{5}', inputs.target, inputs.os, inputs.target, inputs.bazel-config, inputs.use-bazel-addons-on-darwin, github.sha) }} + enableCrossOsArchive: "true" + + - name: Restore cached source-layout addons and executable bits + if: steps.cache.outputs.cache-hit == 'true' + run: node script/restore-cli-build.mjs + + - name: Verify completed runtime build + if: steps.cache.outputs.cache-hit == 'true' + shell: bash + env: + TARGET: ${{ inputs.target }} + run: | + extension= + if [[ "$TARGET" == win32-* ]]; then + extension=.exe + fi + test -s project-build-receipt.txt + test -s "dist-cli/prebuilds/$TARGET/runtime.node" + test -s "dist-cli/prebuilds/$TARGET/cli-native.node" + test -s "dist-cli/prebuilds/$TARGET/copilot-runtime$extension" + test -s "dist-bin/$TARGET/copilot$extension" + + - name: Upload SDK runtime artifact + if: steps.cache.outputs.cache-hit == 'true' + uses: actions/upload-artifact@v7 + with: + name: executable-sdk-${{ inputs.artifact }} + path: | + dist-cli/ + dist-bin/${{ inputs.target }}/ + !dist-cli/sea.blob + !dist-cli/github-copilot-*.tgz + include-hidden-files: true + if-no-files-found: error + retention-days: 1 + build: + needs: cache-relay + if: >- + !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 }} - concurrency: - group: ${{ inputs.shared-cli-build-compatible && format('project-build-sea-{0}-release-{1}-{2}', inputs.os, inputs.bazel-config, github.sha) || format('sdk-runtime-artifact-{0}-{1}', inputs.target, github.sha) }} - queue: max permissions: actions: read contents: read @@ -102,7 +188,8 @@ jobs: src/native/cli/index.d.ts !dist-cli/sea.blob !dist-cli/github-copilot-*.tgz - key: ${{ inputs.shared-cli-build-compatible && format('project-build-v3-sea-{0}-{1}-{2}-release-{3}-{4}-{5}', inputs.os, runner.os, runner.arch, inputs.bazel-config, inputs.use-bazel-addons-on-darwin, github.sha) || format('sdk-project-build-v3-{0}-{1}-{2}-{3}-release-{4}-{5}-{6}', inputs.target, inputs.os, runner.os, runner.arch, inputs.bazel-config, inputs.use-bazel-addons-on-darwin, github.sha) }} + key: ${{ inputs.shared-cli-build-compatible && format('project-build-v4-sea-{0}-{1}-release-{2}-{3}-{4}', inputs.os, inputs.target, inputs.bazel-config, inputs.use-bazel-addons-on-darwin, github.sha) || format('sdk-project-build-v4-{0}-{1}-{2}-release-{3}-{4}-{5}', inputs.target, inputs.os, inputs.target, inputs.bazel-config, inputs.use-bazel-addons-on-darwin, github.sha) }} + enableCrossOsArchive: "true" - name: Restore cached source-layout addons and executable bits if: inputs.runtime-source == 'checkout' && steps.cache.outputs.cache-hit == 'true' @@ -112,6 +199,10 @@ jobs: uses: ./.github/actions/setup-node timeout-minutes: ${{ fromJSON(env.SETUP_NODE_TIMEOUT_MINUTES) }} + - name: Capture verified addon source identity + if: inputs.runtime-source == 'checkout' && steps.cache.outputs.cache-hit != 'true' + run: node --experimental-strip-types script/resolve-cli-build-identity.ts --github-env + - if: inputs.runtime-source == 'checkout' && steps.cache.outputs.cache-hit != 'true' && inputs.prebuilt-addons-artifact == '' && !(runner.os == 'Windows' && inputs.target == 'win32-arm64') uses: ./.github/actions/setup-bazel with: @@ -190,6 +281,7 @@ jobs: !dist-cli/sea.blob !dist-cli/github-copilot-*.tgz key: ${{ steps.cache.outputs.cache-primary-key }} + enableCrossOsArchive: "true" - name: Upload SDK runtime artifact uses: actions/upload-artifact@v7 diff --git a/.github/workflows/sdk-rust.yml b/.github/workflows/sdk-rust.yml index 10b99a6beb..abe13e28bb 100644 --- a/.github/workflows/sdk-rust.yml +++ b/.github/workflows/sdk-rust.yml @@ -25,13 +25,13 @@ permissions: jobs: rust: - name: "Rust (${{ matrix.os }}, default)" + 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-latest"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-latest","windows-latest"]') }} + os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-26-xlarge"]' || inputs.platform == 'win32-x64' && '["windows-latest"]' || '["ubuntu-latest","macos-26-xlarge","windows-latest"]') }} runs-on: ${{ matrix.os }} defaults: run: @@ -45,9 +45,9 @@ jobs: timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} + artifact-name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore && node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" prepare working-directory: . @@ -86,78 +86,57 @@ jobs: working-directory: ${{ inputs.sdk-home }}/test/harness - if: runner.os == 'Windows' run: pwsh.exe -Command "Write-Host 'PowerShell ready'" - - env: + - name: Test out-of-process + id: subprocess + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} RUST_E2E_CONCURRENCY: 4 run: cargo test --no-default-features --features runtime,test-support,derive -- --test-threads=4 --nocapture + - name: Report Linux Rust build resources + if: failure() && runner.os == 'Linux' + run: | + echo "::group::Rust SDK build resources" + df -h . "$RUNNER_TEMP" + df -i . "$RUNNER_TEMP" + free -h + if [ -d target ]; then + du -sh target + fi + echo "::endgroup::" - rust-inprocess: - name: "Rust (${{ matrix.os }}, inprocess)" - if: contains(fromJSON('["all","linux-x64","darwin-arm64"]'), inputs.platform) - strategy: - fail-fast: false - matrix: - os: ${{ fromJSON(inputs.platform == 'linux-x64' && '["ubuntu-latest"]' || inputs.platform == 'darwin-arm64' && '["macos-latest"]' || '["ubuntu-latest","macos-latest"]') }} - runs-on: ${{ matrix.os }} - defaults: - run: - shell: bash - working-directory: ${{ inputs.sdk-home }}/rust - env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - COPILOT_SDK_DEFAULT_CONNECTION: inprocess - steps: - - uses: actions/checkout@v7 - timeout-minutes: 4 - with: - persist-credentials: false - - uses: actions/download-artifact@v8 - with: - name: ${{ runner.os == 'Linux' && format('executable-sdk-{0}-{1}-gnu', runner.os, runner.arch) || format('executable-sdk-{0}-{1}', runner.os, runner.arch) }} - 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: dtolnay/rust-toolchain@stable - with: - toolchain: "1.94.0" - - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ inputs.sdk-home }}/rust - prefix-key: v2-sdk-rust-no-bin - cache-bin: false - - run: npm ci --ignore-scripts - working-directory: ${{ inputs.sdk-home }}/test/harness - - env: + - name: Test in-process smoke + id: inprocess + if: ${{ !cancelled() && (steps.subprocess.outcome == 'success' || steps.subprocess.outcome == 'failure') }} + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + COPILOT_SDK_DEFAULT_CONNECTION: inprocess run: >- cargo test --no-default-features --features test-support,local-runtime,derive --test e2e inprocess::should_start_ping_and_stop_inprocess_client -- --exact --test-threads=1 --nocapture - - env: + - name: Test in-process authentication host + if: ${{ !cancelled() && (steps.inprocess.outcome == 'success' || steps.inprocess.outcome == 'failure') }} + env: COPILOT_HMAC_KEY: ${{ secrets.COPILOT_DEVELOPER_CLI_INTEGRATION_HMAC_KEY }} + COPILOT_SDK_DEFAULT_CONNECTION: inprocess run: >- cargo test --no-default-features --features test-support,local-runtime,derive --test e2e client::should_use_configured_github_host_for_authentication -- --exact --test-threads=1 --nocapture rust-musl-x64: - name: "Rust (Alpine x64, ${{ matrix.transport }})" + name: "Rust (Alpine x64)" if: inputs.platform == 'all' || inputs.platform == 'linuxmusl-x64' runs-on: ubuntu-latest timeout-minutes: 60 - strategy: - fail-fast: false - matrix: - transport: [default, inprocess] steps: - uses: actions/checkout@v7 timeout-minutes: 4 with: persist-credentials: false - - uses: actions/download-artifact@v8 + - uses: ./.github/actions/download-sdk-runtime with: - name: executable-sdk-Linux-X64-musl + artifact-name: executable-sdk-Linux-X64-musl path: ${{ github.workspace }} - run: node "$SDK_HOME/scripts/ci/runtime-artifact.mjs" restore working-directory: . @@ -172,7 +151,6 @@ jobs: image: rust:1.94.0-alpine sdk-root: /workspace/${{ inputs.sdk-home }} workdir: /workspace/${{ inputs.sdk-home }}/rust - transport: ${{ matrix.transport }} command: | rustc --print cfg | grep -Fqx "target_arch=\"x86_64\"" rustc --print cfg | grep -Fqx "target_env=\"musl\"" @@ -197,15 +175,13 @@ jobs: export CARGO_INCREMENTAL=0 export CARGO_PROFILE_TEST_DEBUG=0 cd "$COPILOT_SDK_ROOT/rust" - if [ "$COPILOT_SDK_TEST_TRANSPORT" = "inprocess" ]; then - unset RUST_E2E_CONCURRENCY - cargo test --no-default-features --features test-support,local-runtime,derive \ - --test e2e inprocess::should_start_ping_and_stop_inprocess_client \ - -- --exact --test-threads=1 --nocapture - cargo test --no-default-features --features test-support,local-runtime,derive \ - --test e2e client::should_use_configured_github_host_for_authentication \ - -- --exact --test-threads=1 --nocapture - else - export RUST_E2E_CONCURRENCY=4 - cargo test --no-default-features --features runtime,test-support,derive -- --test-threads=4 --nocapture - fi + status=0 + RUST_E2E_CONCURRENCY=4 cargo test --no-default-features --features runtime,test-support,derive -- --test-threads=4 --nocapture || status=$? + export COPILOT_SDK_DEFAULT_CONNECTION=inprocess + cargo test --no-default-features --features test-support,local-runtime,derive \ + --test e2e inprocess::should_start_ping_and_stop_inprocess_client \ + -- --exact --test-threads=1 --nocapture || status=$? + cargo test --no-default-features --features test-support,local-runtime,derive \ + --test e2e client::should_use_configured_github_host_for_authentication \ + -- --exact --test-threads=1 --nocapture || status=$? + exit "$status" diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index b4ceb876a4..56c165db89 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -4,6 +4,9 @@ name: "SDK build and test" env: COPILOT_BAZEL_ANG_ENABLED: ${{ vars.COPILOT_BAZEL_ANG_ENABLED }} + COPILOT_BAZEL_ANG_SCOPE: ${{ vars.COPILOT_BAZEL_ANG_SCOPE }} + COPILOT_BAZEL_ANG_PR_SAMPLE_PERCENTAGE: ${{ vars.COPILOT_BAZEL_ANG_PR_SAMPLE_PERCENTAGE }} + COPILOT_BAZEL_ANG_PR_SAMPLED_MODE: ${{ vars.COPILOT_BAZEL_ANG_PR_SAMPLED_MODE }} HUSKY: 0 POWERSHELL_UPDATECHECK: Off SETUP_NODE_TIMEOUT_MINUTES: 10 @@ -24,8 +27,150 @@ permissions: contents: read jobs: + detect-sdk-changes: + name: "Detect SDK-impacting changes" + runs-on: ubuntu-slim + outputs: + has_sdk_changes: ${{ steps.full-coverage.outputs.has_sdk_changes || steps.classify.outputs.has_sdk_changes || steps.recover.outputs.has_sdk_changes }} + steps: + - name: Require full coverage outside pull requests + id: full-coverage + if: github.event_name != 'pull_request' + run: echo "has_sdk_changes=true" >> "$GITHUB_OUTPUT" + + - uses: actions/checkout@v7 + 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. + fetch-depth: 2 + persist-credentials: false + + - name: Detect repository layout + id: repository-layout + if: steps.checkout.outcome == 'success' + continue-on-error: true + run: | + trusted_revision="$(git rev-parse "$GITHUB_SHA^1")" + echo "base-sha=$trusted_revision" >> "$GITHUB_OUTPUT" + if git cat-file -e "${trusted_revision}:src/sdk/package.json" 2>/dev/null && + git cat-file -e "${trusted_revision}:script/sea-build.ts" 2>/dev/null; then + echo "layout=runtime" >> "$GITHUB_OUTPUT" + elif git cat-file -e "${trusted_revision}:package.json" 2>/dev/null && + git cat-file -e "${trusted_revision}:nodejs/package.json" 2>/dev/null; then + echo "layout=standalone" >> "$GITHUB_OUTPUT" + else + echo "::warning::Unable to identify the SDK repository layout; requiring full coverage." + exit 1 + fi + + - name: Find SDK-impacting paths + id: filter + if: steps.repository-layout.outcome == 'success' + continue-on-error: true + uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 + with: + base_sha: ${{ steps.repository-layout.outputs.base-sha }} + sha: ${{ github.sha }} + skip_initial_fetch: true + fail_on_initial_diff_error: true + quotepath: false + files_yaml: | + runtime: + - .github/actions/** + - .github/workflows/sdk*.yml + - .github/workflows/sdk*.yaml + - .github/workflows/shared-cli-build.yml + - .github/workflows/publish.yml + - .editorconfig + - .gitattributes + - .nvmrc + - package.json + - package-lock.json + - pnpm-lock.yaml + - sdk-protocol-version.json + - assets/** + - BUILD.bazel + - src/** + - generated/** + - schema/** + - files/** + - third_party/** + - patches/** + - tools/bazel/** + - .cargo/** + - bazel/** + - script/** + - .bazelignore + - .bazelrc + - .bazelversion + - Cargo.lock + - Cargo.toml + - MODULE.bazel + - MODULE.bazel.lock + - pnpm-workspace.yaml + - rust-toolchain.toml + - esbuild.ts + - tsconfig*.json + standalone: + - .github/actions/** + - .github/workflows/sdk*.yml + - .github/workflows/sdk*.yaml + - .github/workflows/shared-cli-build.yml + - .github/workflows/publish.yml + - .editorconfig + - .gitattributes + - .nvmrc + - package.json + - package-lock.json + - pnpm-lock.yaml + - sdk-protocol-version.json + - assets/** + - BUILD.bazel + - nodejs/** + - python/** + - go/** + - dotnet/** + - java/** + - rust/** + - scripts/** + - samples/** + - test/** + - docs/** + - README.md + - vitest.config.ts + + - name: Select layout-specific result + id: classify + if: steps.filter.outcome == 'success' + env: + LAYOUT: ${{ steps.repository-layout.outputs.layout }} + RUNTIME_CHANGED: ${{ steps.filter.outputs.runtime_any_modified }} + STANDALONE_CHANGED: ${{ steps.filter.outputs.standalone_any_modified }} + run: | + if [[ "$LAYOUT" == "runtime" ]]; then + has_sdk_changes="$RUNTIME_CHANGED" + elif [[ "$LAYOUT" == "standalone" ]]; then + has_sdk_changes="$STANDALONE_CHANGED" + else + exit 1 + fi + echo "has_sdk_changes=$has_sdk_changes" >> "$GITHUB_OUTPUT" + + - name: Require full coverage after detection errors + id: recover + if: github.event_name == 'pull_request' && (steps.checkout.outcome != 'success' || steps.repository-layout.outcome != 'success' || steps.filter.outcome != 'success' || steps.classify.outcome != 'success') + run: | + echo "::warning::SDK change detection failed; requiring full coverage." + echo "has_sdk_changes=true" >> "$GITHUB_OUTPUT" + detect-layout: 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 outputs: runtime-source: ${{ steps.detect.outputs.runtime-source }} @@ -49,8 +194,9 @@ jobs: fi # TEMPORARY MERGE QUEUE REDUCTION: pull requests and main pushes retain the - # full platform matrix. Merge groups rerun Linux x64 as the representative - # compatibility lane. Remove the merge_group gates below to restore it. + # full SDK matrix. Merge groups rerun only Linux x64 Node.js out-of-process + # CAPI coverage. Remove the merge_group gates here and in sdk-platform.yml + # and sdk-nodejs.yml to restore the full matrix. build-runtime-windows-x64-addons: name: "Build runtime addons (win32-x64)" needs: detect-layout @@ -127,6 +273,9 @@ jobs: - name: Cross-build Windows x64 addons if: steps.cache.outputs.cache-hit != 'true' env: + # The CI-owned Bazel override is intentionally trusted; without this + # identity the producer stamps an empty source while consumers verify the SHA. + COPILOT_NAPI_ADDONS_BUILD_IDENTITY: ${{ github.sha }} CLI_GIT_COMMIT: ${{ github.sha }} CLI_PACKAGE_NAME: "@github/copilot" CLI_TARGET: win32-x64 @@ -226,6 +375,26 @@ jobs: timeout-minutes: 60 secrets: inherit + build-runtime-linuxmusl-arm64: + name: "Build runtime (linuxmusl-arm64)" + needs: detect-layout + if: github.event_name != 'merge_group' + permissions: + actions: read + contents: read + id-token: write + packages: read + uses: ./.github/workflows/sdk-runtime-artifact.yml + with: + runtime-source: ${{ needs.detect-layout.outputs.runtime-source }} + sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} + os: ubuntu-latest-xl + target: linuxmusl-arm64 + artifact: Linux-ARM64-musl + bazel-config: e2e-no-lto + timeout-minutes: 60 + secrets: inherit + build-runtime-darwin-arm64: name: "Build runtime (darwin-arm64)" needs: detect-layout @@ -239,7 +408,7 @@ jobs: with: runtime-source: ${{ needs.detect-layout.outputs.runtime-source }} sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} - os: macos-latest-xlarge + os: macos-26-xlarge target: darwin-arm64 artifact: macOS-ARM64 bazel-config: e2e-no-lto @@ -289,6 +458,16 @@ jobs: sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} secrets: inherit + sdk-linuxmusl-arm64: + name: "Linux musl ARM64 (Java)" + needs: [detect-layout, build-runtime-linuxmusl-arm64] + if: github.event_name != 'merge_group' + uses: ./.github/workflows/sdk-java.yml + with: + platform: linuxmusl-arm64 + sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} + secrets: inherit + sdk-darwin-arm64: name: "macOS ARM64" needs: [detect-layout, build-runtime-darwin-arm64] @@ -310,15 +489,35 @@ jobs: sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} secrets: inherit - sdk-docs: - name: "Documentation" - needs: detect-layout - if: github.event_name != 'merge_group' - uses: ./.github/workflows/sdk-platform.yml - with: - platform: docs - sdk-home: ${{ needs.detect-layout.outputs.sdk-home }} - secrets: inherit + # Only Linux CAPI gates this rollup; the full SDK aggregate still reports all coverage. + sdk-typescript: + name: sdk-typescript + if: always() + needs: [detect-sdk-changes, sdk-linux-x64] + runs-on: ubuntu-slim + steps: + - name: Check Linux CAPI Node.js SDK result + env: + DETECT_RESULT: ${{ needs.detect-sdk-changes.result }} + EVENT_NAME: ${{ github.event_name }} + HAS_SDK_CHANGES: ${{ needs.detect-sdk-changes.outputs.has_sdk_changes }} + RESULTS: ${{ toJSON(needs) }} + run: | + linux_result=$(jq -r '.["sdk-linux-x64"].result' <<< "$RESULTS") + if [[ "$EVENT_NAME" == "pull_request" && "$DETECT_RESULT" == "success" && "$HAS_SDK_CHANGES" == "false" ]]; then + if [[ "$linux_result" != "skipped" ]]; then + echo "::error::Non-SDK pull request did not skip sdk-linux-x64: $linux_result" + exit 1 + fi + echo "No SDK-impacting changes; Linux CAPI Node.js SDK job skipped" + exit 0 + fi + + result=$(jq -r '.["sdk-linux-x64"].outputs["nodejs-capi-result"]' <<< "$RESULTS") + if [[ "$result" != "success" ]]; then + echo "::error::Linux CAPI Node.js SDK job: expected success, got $result" + exit 1 + fi check-freshness: name: "Check schema and SDK freshness" @@ -401,26 +600,45 @@ jobs: name: "SDK" if: always() needs: + - detect-sdk-changes - build-runtime-windows-x64-addons - build-runtime-linux-x64 - build-runtime-linuxmusl-x64 + - build-runtime-linuxmusl-arm64 - build-runtime-darwin-arm64 - build-runtime-win32-x64 - detect-layout - sdk-linux-x64 - sdk-linuxmusl-x64 + - sdk-linuxmusl-arm64 - sdk-darwin-arm64 - sdk-win32-x64 - - sdk-docs - check-freshness runs-on: ubuntu-slim steps: - name: Check SDK results env: + DETECT_RESULT: ${{ needs.detect-sdk-changes.result }} EVENT_NAME: ${{ github.event_name }} + HAS_SDK_CHANGES: ${{ needs.detect-sdk-changes.outputs.has_sdk_changes }} RESULTS: ${{ toJSON(needs) }} RUNTIME_SOURCE: ${{ needs.detect-layout.outputs.runtime-source }} run: | + if [[ "$EVENT_NAME" == "pull_request" && "$DETECT_RESULT" == "success" && "$HAS_SDK_CHANGES" == "false" ]]; then + unexpected=$(jq -r ' + to_entries[] + | select(.key != "detect-sdk-changes" and .value.result != "skipped") + | "\(.key): \(.value.result)" + ' <<< "$RESULTS") + if [[ -n "$unexpected" ]]; then + echo "::error::Non-SDK pull request did not skip all SDK jobs:" + echo "$unexpected" + exit 1 + fi + echo "No SDK-impacting changes; SDK build and test matrix skipped" + exit 0 + fi + # TEMPORARY MERGE QUEUE REDUCTION: keep this branch aligned with the # gated jobs above until the full merge-group matrix is restored. if [[ "$EVENT_NAME" == "merge_group" ]]; then @@ -433,12 +651,13 @@ jobs: skipped_jobs=( build-runtime-windows-x64-addons build-runtime-linuxmusl-x64 + build-runtime-linuxmusl-arm64 build-runtime-darwin-arm64 build-runtime-win32-x64 sdk-linuxmusl-x64 + sdk-linuxmusl-arm64 sdk-darwin-arm64 sdk-win32-x64 - sdk-docs ) for job in "${required_jobs[@]}"; do if [[ "$(jq -r --arg job "$job" '.[$job].result' <<< "$RESULTS")" != "success" ]]; then @@ -454,7 +673,7 @@ jobs: exit 1 fi done - echo "Linux x64 SDK compatibility and generated clients passed" + echo "Linux x64 Node.js SDK compatibility and generated clients passed" exit 0 fi @@ -469,6 +688,7 @@ jobs: failures=$(jq -r --arg runtime_source "$RUNTIME_SOURCE" ' to_entries[] | select(.value.result != "success") + | select(.key != "detect-sdk-changes") | select( $runtime_source != "published" or .key != "build-runtime-windows-x64-addons" diff --git a/BUILD.bazel b/BUILD.bazel index 792fd2f519..4fa953fbf5 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -1,8 +1,10 @@ SHARED_CODEGEN_INPUTS = [ "nodejs/scripts/releaseArtifacts.ts", "nodejs/src/cliVersion.ts", + "scripts/codegen/legacy-parameters.ts", "scripts/codegen/package-lock.json", "scripts/codegen/package.json", + "scripts/codegen/extensible-enums.ts", "scripts/codegen/utils.ts", "scripts/runtime-layout.mjs", "scripts/runtime-release.mjs", @@ -222,6 +224,7 @@ genrule( "java/scripts/codegen/package-lock.json", "java/scripts/codegen/package.json", "nodejs/package.json", + "scripts/codegen/legacy-parameters.ts", "scripts/runtime-layout.mjs", "scripts/runtime-release.mjs", ], diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad9514a939..74519107b4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,12 +101,21 @@ download missing tools on first use. ### Prepare project dependencies Build tasks restore the selected SDK's dependencies: Node runs -`npm ci --ignore-scripts`, Python runs `uv sync --all-extras --dev`, and the +`npm ci --ignore-scripts --include=dev`, Python runs `uv sync --all-extras --dev`, and the other native build tools restore their project dependencies. In the runtime layout, build/test/generate tasks also install the codegen npm dependencies and refresh public schemas and the selected projections through Bazel. This preparation can update generated source files. +Node SDK builds and codegen preparation explicitly include development +dependencies even with `NODE_ENV=production` or `npm_config_omit=dev`, because +these packages provide the build tools. They skip installation when the npm +arguments, `package.json`, and `package-lock.json` match the last successful install's +`node_modules/.copilot-sdk-install-stamp`. Changing either file, removing +`node_modules` or its stamp, or an unsuccessful install requires a fresh install. +The runtime root's `build:sdk:link` uses the same stamp for Node dependencies. +Java codegen dependencies are installed only when Java is selected. + Tests and checks need additional tools that `pnpm install` does not provide. Before Node, Python, Go, .NET, or Rust SDK tests, prepare Node tooling and the shared replay harness if they are not already installed: @@ -116,6 +125,15 @@ npm --prefix nodejs ci --ignore-scripts npm --prefix test/harness ci --ignore-scripts ``` +Node tests, including the unit-only profile, run generator subprocesses and +require the codegen package's own dependencies. Prepare these before invoking +Node tests directly or from a standalone SDK checkout (runtime build/test tasks +already prepare them): + +```bash +npm --prefix scripts/codegen ci --ignore-scripts +``` + The full Node test task also runs the corrections-script tests: ```bash @@ -133,6 +151,7 @@ Substitute `nodejs`, `python`, `go`, `dotnet`, `java`, or `rust` for ` | From the SDK root | Scope | | --- | --- | | `npm run build:` | Build one SDK; `npm run build` builds all six. | +| `npm run build:default` | Build the Node and Rust SDKs, in that order. | | `npm run test:` | Run that language's suite; `npm test` runs all six. | | `npm run test:default` | Node SDK unit tests and default-feature Rust SDK tests with `test-support`. | | `npm run check:` | Language-specific checks. Java includes `verify` (tests), .NET includes a solution build, and Rust includes Clippy and nightly formatting. | @@ -149,11 +168,17 @@ sources. Runtime-root aliases live in the parent runtime's SDK-local commands live in [package.json](package.json) and [run-tasks.mjs](scripts/run-tasks.mjs). +The runtime root's `pnpm run build` builds the CLI first, then invokes +`pnpm run build:sdk:default`. This default SDK profile prepares Node and Rust +projections together once and does not rebuild the CLI or build the other four SDKs. +Node declaration generation uses TypeScript's incremental build information in +`nodejs/dist/tsconfig.tsbuildinfo`; removing `dist` also clears that state. + In a runtime checkout, SDK tests request a current host `build:cli` before -running; Java and aggregate SDK builds also prepare the CLI. Unchanged Bazel +running; Java and all-six SDK builds also prepare the CLI. Unchanged Bazel actions remain cached. The Rust SDK build uses Bazel, but its tests use the independent SDK Cargo toolchain. Standalone tasks instead use the SDK's pinned -published runtime inputs. Do not work around a missing checkout artifact by +published runtime inputs and build Rust with Cargo's `--all-features`. Do not work around a missing checkout artifact by changing release pins or switching to a published runtime. Rust's `test:rust` and `test:default` run `cargo test --features test-support`; @@ -291,7 +316,12 @@ additional feature/acquisition choices documented in [rust/AGENTS.md](rust/AGENT ### Documentation checks -API snippet validation is separate from SDK tests. From the SDK root: +[SDK CI](.github/workflows/sdk.yml) validates Node.js, Python, Go, .NET, and Java +API snippets after successful tests in their standard Linux jobs (JDK 25 for Java). +These checks run on pull requests, pushes to `main`, and manual workflow runs, +but not merge groups. A documentation failure fails the corresponding language job. + +To validate snippets without running SDK tests, run these commands from the SDK root: ```bash npm --prefix scripts/docs-validation ci @@ -302,8 +332,15 @@ uv run --locked --project python npm run docs:python ``` This extracts and validates snippets for Node.js, Python, Go, .NET, or Java. -Java's current docs validator calls `mvn` directly, -so this task also needs Maven 3.9+ on PATH. There is no `docs:rust` facade; +`docs:java` installs the SDK once through its Maven wrapper, with tests, replay +harness setup, and native downloads disabled, then compiles the snippets using +the same wrapper. CI supplies that installation in its existing post-test step; +the validator does not reinstall it. A separate Maven installation is not required. +Java validation also compiles the exact [README Quick Start](java/README.md#quick-start). +The regular Maven integration suite checks a standalone consumer JAR with a manifest +classpath and runtime dependencies, without a live model, credentials, or native runtime. +These checks replace the former agent-driven Java smoke workflow. +There is no `docs:rust` facade; follow [the Rust SDK workflow](.github/workflows/sdk-rust.yml) for rustdoc. ### Recording and replaying SDK tests @@ -319,6 +356,48 @@ 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. +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. + +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 +checks. After preparing the runtime as described above, run a sweep from the +SDK root: + +```bash +COPILOT_SDK_E2E_BACKEND=anthropic-messages GITHUB_ACTIONS=true \ + npm --prefix nodejs test -- test/e2e +``` + +Use `openai-completions` or `openai-responses` for the other sweeps; unset the +variable or use `capi` for the normal suite. BYOK uses the shared captures +through the corresponding protocol adapter and never forwards replay misses +to a live provider. Use the fixture's `createClient()` for secondary clients +that create or resume model-backed sessions. Tests that require CAPI or own +their provider/request-handler setup stay in the normal job, with exclusions +in `vitest.config.ts` or individual `it.skipIf(isByokBackend)` cases. Other SDK +languages retain their language-specific provider tests in their normal suites. + +The Node.js Vitest global setup bundles the shared replay proxy once per run +into Vitest's project-owned temporary directory (and refreshes it on watch +reruns). Each E2E context launches that bundle directly with the test runner's +Node executable, without an npm, shell, or TypeScript-loader subprocess. +Focused Vitest commands use the same setup; no separate proxy build is needed. +If startup or shutdown cleanup cannot confirm that the child exited, `CapiProxy` +retains the child handle so callers can retry cleanup with `stop()`. +Once shutdown is acknowledged, it waits for the child to finish flushing captures +and exit rather than applying a forced-termination timeout to the flush. + Owned-stdio shutdown regressions share `test/harness/stdio-shutdown-runtime.cjs` across all six SDKs. Launch it with Node and arguments ` `. The fixture acknowledges @@ -329,6 +408,12 @@ failed-startup cleanup. Keep those lifecycle expectations aligned when changing an SDK transport; test watchdogs must allow all cleanup phases their separate budgets, rather than treating the graceful-exit timeout as a total shutdown cap. +Java also exercises the opt-in `shutdown-error` mode. It rejects +`runtime.shutdown`, writes `.eof` when stdin closes, and holds +cleanup-marker creation and process exit until the test creates +`.release`. This handshake verifies that a failed shutdown RPC +still allows EOF-driven finalization before forced cleanup. + The shared harness records real inference responses under `test/snapshots`. Record new captures with `GITHUB_TOKEN` set and `GITHUB_ACTIONS` unset; never author model responses by hand. Rerun with `GITHUB_ACTIONS=true` and real @@ -363,10 +448,12 @@ need copies across languages: Typed cases call the public idiomatic APIs: Node/Zod, C# generics, Python/Pydantic, Go generics, Java annotated records using the existing tool schema generator, -and Rust generics with `derive`/schemars. The tool/follow-up case also checks the -actual provider request's inferred schema, so a recorded JSON response alone -cannot mask missing schema forwarding. Explicit-schema and event-stream cases -exercise the corresponding raw public APIs instead. +and Rust generics with `derive`/schemars. The CAPI leg's tool/follow-up case also +checks the configured OpenAI provider request's inferred schema, so a recorded +JSON response alone cannot mask missing schema forwarding there. BYOK sweeps +retain the typed-result assertions without assuming the same wire-format +encoding. Explicit-schema and event-stream cases exercise the corresponding +raw public APIs instead. The existing suite in each language also checks rejection before admission and zero provider calls for oversized schemas and typed immediate steering. diff --git a/docs/developer-docs/secrets.md b/docs/developer-docs/secrets.md index 1218f96254..b09ddd213c 100644 --- a/docs/developer-docs/secrets.md +++ b/docs/developer-docs/secrets.md @@ -17,7 +17,7 @@ These secrets are used by the authoritative SDK build/test workflow. These secrets power the GitHub Agentic Workflows (gh-aw) used for issue triage, code generation, and release automation. * **`COPILOT_GITHUB_TOKEN`**: GitHub OAuth token consumed by the Copilot CLI for AI authentication. Required by all agentic workflows when invoking `copilot` for AI inference. - * Workflows: `issue-triage.lock.yml`, `issue-classification.lock.yml`, `handle-bug.lock.yml`, `handle-enhancement.lock.yml`, `handle-question.lock.yml`, `handle-documentation.lock.yml`, `java-codegen-check.yml`, `java-codegen-fix.lock.yml`, `java-smoke-test.yml`, `java-adapt-handwritten-code-to-accept-upgrade-changes.lock.yml`, `release-changelog.lock.yml`, `cross-repo-issue-analysis.lock.yml` + * Workflows: `issue-triage.lock.yml`, `issue-classification.lock.yml`, `handle-bug.lock.yml`, `handle-enhancement.lock.yml`, `handle-question.lock.yml`, `handle-documentation.lock.yml`, `java-codegen-check.yml`, `java-codegen-fix.lock.yml`, `java-adapt-handwritten-code-to-accept-upgrade-changes.lock.yml`, `release-changelog.lock.yml`, `cross-repo-issue-analysis.lock.yml` * **`GH_AW_GITHUB_TOKEN`**: Optional GitHub token override for repository operations (reading code, creating pull requests, and making GitHub API calls). If unset, workflows use the automatic `GITHUB_TOKEN`. * Workflows: `issue-triage.lock.yml`, `issue-classification.lock.yml`, `handle-bug.lock.yml`, `handle-enhancement.lock.yml`, `handle-question.lock.yml`, `handle-documentation.lock.yml`, `java-codegen-fix.lock.yml`, `java-adapt-handwritten-code-to-accept-upgrade-changes.lock.yml`, `release-changelog.lock.yml`, `cross-repo-issue-analysis.lock.yml` diff --git a/docs/features/README.md b/docs/features/README.md index 5ea070a5aa..3cf785ecb9 100644 --- a/docs/features/README.md +++ b/docs/features/README.md @@ -11,6 +11,7 @@ These guides cover the capabilities you can add to your Copilot SDK application. | [The Agent Loop](./agent-loop.md) | How the CLI processes a prompt—the tool-use loop, turns, and completion signals | | [Hooks](./hooks.md) | Intercept and customize session behavior—control tool execution, transform results, handle errors | | [Custom Agents](./custom-agents.md) | Define specialized sub-agents with scoped tools and instructions | +| [Changing Tools](./changing-tools.md) | Replace the tools a client supplies to a live session | | [Fleet Mode](./fleet-mode.md) | Dispatch multiple sub-agents in parallel for large, independent workstreams | | [MCP Servers](./mcp.md) | Integrate Model Context Protocol servers for external tool access | | [Skills](./skills.md) | Load reusable prompt modules from directories | diff --git a/docs/features/changing-tools.md b/docs/features/changing-tools.md new file mode 100644 index 0000000000..93f0938ec9 --- /dev/null +++ b/docs/features/changing-tools.md @@ -0,0 +1,372 @@ +# Changing tools during a session + +An application can replace the tools it supplies to a live session without recreating it. For example, a web page can offer different tools as the user navigates, or a client that takes over a session can bring its own tools. Each SDK replaces the client's complete tool set and the handlers that serve it in one call. + +> **Experimental.** Tool replacement wraps the experimental `session.tools.set` RPC and requires a runtime that supports it. + +## Replace this client's tools + +Pass the complete set of tools this client should supply. It replaces the tools the client supplied when the session was created or resumed, or in an earlier replacement. Built-in, MCP, and plugin tools, and tools that other clients connected to the same session supply, are unaffected. Pass an empty list to remove all of this client's tools. + +Tools are defined exactly as they are for creating a session, and a tool without a handler behaves the same way it does there. + +
+TypeScript + + +```typescript +import { CopilotClient, approveAll, defineTool } from "@github/copilot-sdk"; +import { z } from "zod"; + +declare function findIssues(query: string): Promise; + +async function main() { + const client = new CopilotClient(); + const session = await client.createSession({ onPermissionRequest: approveAll }); + + const searchIssues = defineTool("search_issues", { + description: "Search the issues shown on the current page", + parameters: z.object({ query: z.string().describe("Search text") }), + handler: async ({ query }) => findIssues(query), + }); + + // Supply only the tools the current page provides. + await session.setTools([searchIssues]); + + // Remove all of this client's tools. + await session.setTools([]); +} + +main(); +``` + + +```typescript +import { defineTool } from "@github/copilot-sdk"; +import { z } from "zod"; + +const searchIssues = defineTool("search_issues", { + description: "Search the issues shown on the current page", + parameters: z.object({ query: z.string().describe("Search text") }), + handler: async ({ query }) => findIssues(query), +}); + +// Supply only the tools the current page provides. +await session.setTools([searchIssues]); + +// Remove all of this client's tools. +await session.setTools([]); +``` + +
+
+Python + + +```python +from pydantic import BaseModel, Field + +from copilot import CopilotClient +from copilot.session import PermissionHandler +from copilot.tools import define_tool + + +async def find_issues(query: str) -> str: + return query + + +class SearchIssuesParams(BaseModel): + query: str = Field(description="Search text") + + +@define_tool(description="Search the issues shown on the current page") +async def search_issues(params: SearchIssuesParams) -> str: + return await find_issues(params.query) + + +async def main() -> None: + client = CopilotClient() + session = await client.create_session(on_permission_request=PermissionHandler.approve_all) + + # Supply only the tools the current page provides. + await session.set_tools([search_issues]) + + # Remove all of this client's tools. + await session.set_tools([]) +``` + + +```python +from pydantic import BaseModel, Field +from copilot.tools import define_tool + +class SearchIssuesParams(BaseModel): + query: str = Field(description="Search text") + +@define_tool(description="Search the issues shown on the current page") +async def search_issues(params: SearchIssuesParams) -> str: + return await find_issues(params.query) + +# Supply only the tools the current page provides. +await session.set_tools([search_issues]) + +# Remove all of this client's tools. +await session.set_tools([]) +``` + +
+
+Go + + +```go +package main + +import ( + "context" + "log" + + copilot "github.com/github/copilot-sdk/go" +) + +type SearchIssuesParams struct { + Query string `json:"query" jsonschema:"Search text"` +} + +func findIssues(query string) (string, error) { + return query, nil +} + +func main() { + ctx := context.Background() + client := copilot.NewClient(nil) + session, err := client.CreateSession(ctx, &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + }) + if err != nil { + log.Fatal(err) + } + + searchIssues := copilot.DefineTool( + "search_issues", + "Search the issues shown on the current page", + func(params SearchIssuesParams, inv copilot.ToolInvocation) (string, error) { + return findIssues(params.Query) + }, + ) + + // Supply only the tools the current page provides. + if err := session.SetTools(ctx, []copilot.Tool{searchIssues}); err != nil { + log.Fatal(err) + } + + // Remove all of this client's tools. + if err := session.SetTools(ctx, nil); err != nil { + log.Fatal(err) + } +} +``` + + +```go +type SearchIssuesParams struct { + Query string `json:"query" jsonschema:"Search text"` +} + +searchIssues := copilot.DefineTool( + "search_issues", + "Search the issues shown on the current page", + func(params SearchIssuesParams, inv copilot.ToolInvocation) (string, error) { + return findIssues(params.Query) + }, +) + +// Supply only the tools the current page provides. +if err := session.SetTools(ctx, []copilot.Tool{searchIssues}); err != nil { + log.Fatal(err) +} + +// Remove all of this client's tools. +if err := session.SetTools(ctx, nil); err != nil { + log.Fatal(err) +} +``` + +
+
+.NET + + +```csharp +#pragma warning disable GHCP001 +using System.ComponentModel; +using GitHub.Copilot; +using Microsoft.Extensions.AI; + +await using var client = new CopilotClient(); +await using var session = await client.CreateSessionAsync(new SessionConfig +{ + OnPermissionRequest = PermissionHandler.ApproveAll, +}); + +var searchIssues = CopilotTool.DefineTool( + ([Description("Search text")] string query) => FindIssues(query), + factoryOptions: new AIFunctionFactoryOptions + { + Name = "search_issues", + Description = "Search the issues shown on the current page", + }); + +// Supply only the tools the current page provides. +await session.SetToolsAsync([searchIssues]); + +// Remove all of this client's tools. +await session.SetToolsAsync([]); + +static string FindIssues(string query) => query; +#pragma warning restore GHCP001 +``` + + +```csharp +var searchIssues = CopilotTool.DefineTool( + ([Description("Search text")] string query) => FindIssues(query), + factoryOptions: new AIFunctionFactoryOptions + { + Name = "search_issues", + Description = "Search the issues shown on the current page", + }); + +// Supply only the tools the current page provides. +await session.SetToolsAsync([searchIssues]); + +// Remove all of this client's tools. +await session.SetToolsAsync([]); +``` + +`SetToolsAsync` raises the `GHCP001` experimental diagnostic, which you suppress with `#pragma warning disable GHCP001` or a project-level `GHCP001`. + +
+
+Java + + +```java +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import com.github.copilot.AllowCopilotExperimental; +import com.github.copilot.CopilotClient; +import com.github.copilot.CopilotSession; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.SessionConfig; +import com.github.copilot.rpc.ToolDefinition; + +@AllowCopilotExperimental +public class ChangingToolsExample { + public static void main(String[] args) throws Exception { + try (var client = new CopilotClient()) { + CopilotSession session = client + .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)) + .get(); + + var searchIssues = ToolDefinition.create( + "search_issues", + "Search the issues shown on the current page", + Map.of( + "type", "object", + "properties", Map.of("query", Map.of("type", "string", "description", "Search text")), + "required", List.of("query")), + invocation -> CompletableFuture.completedFuture( + findIssues((String) invocation.getArguments().get("query")))); + + // Supply only the tools the current page provides. + session.setTools(List.of(searchIssues)).get(); + + // Remove all of this client's tools. + session.setTools(List.of()).get(); + } + } + + private static String findIssues(String query) { + return query; + } +} +``` + + +```java +var searchIssues = ToolDefinition.create( + "search_issues", + "Search the issues shown on the current page", + Map.of( + "type", "object", + "properties", Map.of("query", Map.of("type", "string", "description", "Search text")), + "required", List.of("query")), + invocation -> CompletableFuture.completedFuture( + findIssues((String) invocation.getArguments().get("query")))); + +// Supply only the tools the current page provides. +session.setTools(List.of(searchIssues)).get(); + +// Remove all of this client's tools. +session.setTools(List.of()).get(); +``` + +`setTools` is `@CopilotExperimental`, so the consuming class or method must opt in with `@AllowCopilotExperimental`. See [Using experimental APIs](../../java/README.md#using-experimental-apis). + +
+
+Rust + + + +```rust +#[derive(Deserialize, JsonSchema)] +struct SearchIssuesParams { + /// Search text + query: String, +} + +let search_issues = define_tool( + "search_issues", + "Search the issues shown on the current page", + |_inv, params: SearchIssuesParams| async move { + Ok(ToolResult::Text(find_issues(¶ms.query).await)) + }, +); + +// Supply only the tools the current page provides. +session.set_tools(vec![search_issues]).await?; + +// Remove all of this client's tools. +session.set_tools(Vec::new()).await?; +``` + +
+ +## When the new tools take effect + +- **Model requests.** The runtime offers the new tools from the agent's next model request. That request can be part of a turn that is already in progress. +- **Handlers.** As soon as the runtime accepts the replacement, every tool call the session dispatches uses the new handlers. Calls that are already running finish on the handlers that started them. +- **Rejection.** If the runtime rejects the replacement, the call fails and the previous tools and handlers stay in place. The runtime rejects tool names with invalid characters and names that another connected client already supplies. +- **Ordering.** Concurrent replacements on the same session are applied one at a time, in the order they're made. +- **Cancellation.** In SDKs that let you cancel the call, cancelling it while an earlier replacement is still in flight sends nothing. Once the request is sent, cancelling only stops the wait: if the runtime accepts the replacement, the new handlers still take effect. + +## Replacing tools during a turn + +A model request that is already in flight was made with the previous tools, so the agent can still call a tool you just removed. The session doesn't answer that call, because another connected client might supply a tool with the same name, so the call can stay pending until the turn is aborted. + +If a running turn might still call a tool you remove, replace tools while the session is idle, for example after the `session.idle` event, or abort the turn first. Replacing a tool's handler under the same name is safe during a turn: calls dispatched after the runtime accepts the replacement use the new handler. + +## SDK reference + +| SDK | Method | +|---|---| +| TypeScript | `session.setTools(tools)` | +| Python | `await session.set_tools(tools)` | +| Go | `session.SetTools(ctx, tools)` | +| .NET | `await session.SetToolsAsync(tools, cancellationToken)` | +| Java | `session.setTools(tools)`, which returns a `CompletableFuture` | +| Rust | `session.set_tools(tools).await` | diff --git a/docs/features/fleet-mode.md b/docs/features/fleet-mode.md index 891a8ef04f..9a9547c8b9 100644 --- a/docs/features/fleet-mode.md +++ b/docs/features/fleet-mode.md @@ -243,7 +243,9 @@ This pattern gives every worker a clear owner and lets the parent session reason Fleet mode invokes sub-agents through the runtime's task mechanism. The runtime emits hook activity for sub-agent tool calls: the runtime 1.0.52 changelog notes that `preToolUse`, `postToolUse`, `subagentStart`, and `subagentStop` fire correctly for sub-agent tool calls. -A dedicated SDK hook callback for `subagentStart` or `subagentStop` was not found in the public SDK surface on this branch. SDK consumers can observe sub-agent activity through the generic session event stream, which includes events such as `subagent.started`, `subagent.completed`, `subagent.failed`, `subagent.selected`, and `subagent.deselected`. +All six SDKs expose typed sub-agent lifecycle hooks: Node.js uses `onSubagentStart` and `onSubagentStop`, Python and Rust use `on_subagent_start` and `on_subagent_stop`, Go and .NET use `OnSubagentStart` and `OnSubagentStop`, and Java uses `setOnSubagentStart` and `setOnSubagentStop`. Register them when creating or resuming a session. The start hook can prepend `additionalContext` to the child's first prompt; the stop hook can request another child turn with a block reason or replace the response reported to the parent. + +For passive status updates, subscribe to the generic session event stream. It includes `subagent.started`, `subagent.completed`, `subagent.failed`, `subagent.selected`, and `subagent.deselected`:
Node.js / TypeScript @@ -338,7 +340,7 @@ Keep plugin-provided sub-agent types narrow and descriptive so the orchestrator * Fleet mode is exposed through generated session RPC bindings and is marked experimental in several SDKs. * The SQL todos pattern is the canonical coordination model in the runtime guidance, but whether it is a stable extensibility contract for SDK consumers is still an open question. -* `subagentStart` and `subagentStop` are runtime hook names; this branch exposes sub-agent lifecycle to SDK consumers through the generic session event stream, not dedicated hook callbacks. +* Sub-agent lifecycle hook inputs identify the parent session but do not yet carry a shared tool-call ID for correlating with `subagent.*` events. * Plugin sub-agent registration is configured at the runtime layer through `--plugin-dir`; no SDK-level plugin registration helper was verified on this branch. * Java native typed bindings for `session.fleet.start` were not found in the Java SDK source on this branch. * Fleet mode does not remove the need for parent-agent review. Parallel workers can produce inconsistent assumptions that the orchestrator must reconcile. diff --git a/docs/features/mcp.md b/docs/features/mcp.md index 19f12e285b..4be11f2452 100644 --- a/docs/features/mcp.md +++ b/docs/features/mcp.md @@ -26,6 +26,44 @@ The SDK supports two types of MCP servers: ## Configuration +### Inspect configuration and lifecycle + +`session.rpc.mcp.listConfigured()` is a parameterless, passive inventory read. +It does not start or restart servers, authenticate, apply sandbox policy, or +wait for server startup. Configuration discovery and policy filtering determine +which servers appear and their `enabled` values. + +This experimental RPC requires an aligned SDK/runtime release. Its earlier +prerelease response allowed `live` to be omitted; SDKs using the required +lifecycle contract do not support that older response shape. When supplying +an external runtime, update it alongside the SDK. The overall protocol version +is unchanged and does not identify this experimental contract revision. + +Every entry includes `live.status`. A configured server with no materialized +runtime server reports the existing `not_configured` status, even when +`enabled` is `true`. A runtime server that was explicitly stopped reports +`stopped`. Materialized servers report their actual lifecycle, including +`pending`, `connected`, `needs-auth`, and `failed`; failures include `live.error` +when available. + +Configuration and lifecycle describe different facts. If a server's URL, +authentication, or configuration source changes while an old connection remains +alive, inventory still reports that connection's actual lifecycle. A +`connected` status does not mean the latest configuration has been applied. +The runtime owns this observation; clients must not substitute cached state +based only on the server name. + +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 +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 +servers or override disabled state, authentication requirements, sandbox policy, +or configuration-source precedence. + ### Node.js / TypeScript ```typescript @@ -183,6 +221,57 @@ On session creation and a **cold** resume, disabled servers are not started and the runtime does not initiate their authentication. A resident resume cannot undo a server that the runtime has already spawned. Names are matched exactly. +## Listing and retrieving prompts + +The experimental generated MCP namespace exposes the wire JSON-RPC methods +`session.mcp.prompts.list` and `session.mcp.prompts.get`. Use the language-specific +SDK accessors below to call them. These methods target one connected server in the +current session; they do not combine results from multiple servers. + +| SDK | List prompts | Get a prompt | +| --- | --- | --- | +| Node.js | `session.rpc.mcp.prompts.list(...)` | `session.rpc.mcp.prompts.get(...)` | +| Python | `session.rpc.mcp.prompts.list(...)` | `session.rpc.mcp.prompts.get(...)` | +| Go | `session.RPC.MCP.Prompts().List(...)` | `session.RPC.MCP.Prompts().Get(...)` | +| .NET | `session.Rpc.Mcp.Prompts.ListAsync(...)` | `session.Rpc.Mcp.Prompts.GetAsync(...)` | +| Java | `session.getRpc().mcp.prompts.list(...)` | `session.getRpc().mcp.prompts.get(...)` | +| Rust | `session.rpc().mcp().prompts().list(...)` | `session.rpc().mcp().prompts().get(...)` | + +Listing requires `serverName` and accepts an optional opaque `cursor`. Each +call returns one page of typed prompt definitions and an optional `nextCursor`. +Pass that cursor in another list call to request the next page. Definitions +include names, optional titles and descriptions, and argument definitions. +An omitted argument `required` flag remains distinct from `false`. + +Getting a prompt requires `serverName` and `promptName`, with an optional +`arguments` dictionary whose values are strings. Omitting `arguments` leaves the +MCP request's arguments absent; passing `{}` sends an explicitly empty dictionary. +The runtime preserves this distinction for the server. The result contains an optional +description and ordered messages with typed roles and opaque JSON `content`. +Inspect each content block's `type` before interpreting it; content is not +flattened into text. Nested resources, annotations, metadata, unfamiliar content +types, and additional content fields retain their JSON structure. Server +extensions on typed descriptors, messages, and result envelopes are available under +`additionalProperties`. A server extension itself named `additionalProperties` +is preserved as an entry inside that map, not merged into it. +The protocol's top-level `resultType: "complete"` discriminator is consumed by +the runtime; it is not returned as an SDK field or a server extension. +Requests fail explicitly if the runtime cannot preserve the raw response, +including stdio response frames exceeding the 1 MiB capture limit. + +The returned prompt messages are not automatically sent to the model or used to +execute tools or fetch referenced resources. Your application decides how to +use the result. +While generating that result, an MCP server can request sampling or elicitation +through multi round-trip continuations. These requests use the same configured +host responders as other MCP operations; prompt retrieval does not grant +additional permission or bypass the host's decision. A missing responder or +responder error fails the request, and an elicitation decline is returned to the +server unchanged. State-only continuations do not invoke either responder. + +After an existing `mcp.prompts.list_changed` session event, list the named +server's prompts again to refresh your application's view. + ## Tool configuration You can control which tools are available to an MCP server using the `tools` field. @@ -295,6 +384,53 @@ directories for different applications. | `tools` | `string[]` | No | Tools to enable | | `timeout` | `number` | No | Timeout in milliseconds | +## OAuth ownership for remote servers + +Choose one OAuth ownership model for each authentication attempt: + +1. **Runtime-managed loopback**: Call `session.mcp.oauth.login` without a + redirect URI. The runtime opens a local callback listener and owns discovery, + PKCE, state validation, token exchange, persistence, refresh, and reconnect. +1. **Runtime-managed hosted callback**: Call `session.mcp.oauth.login` with a + trusted public HTTPS redirect URI. Your host receives the callback, but the + runtime continues to own the OAuth protocol and credentials. +1. **Host-managed OAuth**: Use the SDK's MCP authentication request handler and + return a host-acquired access token. Your host owns authorization and refresh. + +Do not register a host-managed OAuth handler for an authentication attempt that +uses a runtime-managed hosted callback. Wait until the remote server enters the +`needs-auth` state, then call the generated `session.rpc.mcp.oauth.login` method +directly with the server name and callback URI: + +```jsonc +{ + "serverName": "remote-mcp", + "redirectUri": "https://agent.example.com/oauth/callback" +} +``` + +The selected static, CIMD, or dynamically registered OAuth client must advertise +that exact URI. The URI must use HTTPS and must not contain a query or fragment. +When browser interaction is required, the result contains `authorizationUrl` +and `authorizationId`. Open `authorizationUrl` in the user's browser and retain +`authorizationId` with the target session. + +After the authorization server redirects to your endpoint, call the generated +`session.rpc.mcp.oauth.complete` method: + +```jsonc +{ + "authorizationId": "", + "callbackUrl": "https://agent.example.com/oauth/callback?code=...&state=..." +} +``` + +Construct `callbackUrl` from the configured public redirect origin and path plus +the callback's original query. Do not pass an internal service URL or trust +client-supplied `Forwarded` or `X-Forwarded-*` headers. The runtime validates the +origin, port, path, state, and OAuth response before accepting delivery. Token +exchange, persistence, and MCP reconnect then continue asynchronously. + ## Session-scoped MCP diagnostics Set `diagnostics: { sources: { mcp: { level: "debug" } } }` when you create or diff --git a/docs/features/plugin-directories.md b/docs/features/plugin-directories.md index 456ef88e7f..393fc4c048 100644 --- a/docs/features/plugin-directories.md +++ b/docs/features/plugin-directories.md @@ -422,6 +422,63 @@ for (const plugin of plugins.plugins) { Plugins loaded via `--plugin-dir` appear in this list with their cache path set to the directory you provided. Marketplace installs are tagged with their registry source. +## Retrying plugins required by managed settings + +When managed settings require plugins through `enabledPlugins`, the runtime installs or updates them before chat messages. If that fails, the session emits a `session.warning` with `warningType: "managed_plugins"` and waits an hour, or until the managed settings change, before trying again. The experimental `session.rpc.plugins.retryManaged()` tries again right away, for example from a Retry action next to that warning. + +Call it with no arguments to retry every plugin the managed settings require, or pass `plugin@marketplace` specs in `plugins` to retry only those. The result has one entry per plugin with a `status`: `installed`, `updated`, `already_present`, `failed`, `deferred`, or `not_required`. `failed` and `deferred` entries include an `error`. The session also emits the usual `session.info` progress and `session.warning` failure events. + +
+Node.js / TypeScript + + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +async function main() { + const client = new CopilotClient(); + await client.start(); + const session = await client.createSession({ + onPermissionRequest: async () => ({ kind: "approve-once" }), + }); + + // Retry every plugin the managed settings require, for example from a Retry action. + const result = await session.rpc.plugins.retryManaged(); + for (const entry of result.plugins) { + console.log(`${entry.spec}: ${entry.status}${entry.error ? ` (${entry.error})` : ""}`); + } + + // Or retry only the required plugins that are still missing. + const { plugins } = await session.rpc.plugins.list(); + const missing = plugins + .filter((plugin) => plugin.managedDesiredEnabled === true && plugin.installed === false) + .map((plugin) => `${plugin.name}@${plugin.marketplace}`); + await session.rpc.plugins.retryManaged({ plugins: missing }); +} + +main(); +``` + + +```typescript +// Retry every plugin the managed settings require, for example from a Retry action. +const result = await session.rpc.plugins.retryManaged(); +for (const entry of result.plugins) { + console.log(`${entry.spec}: ${entry.status}${entry.error ? ` (${entry.error})` : ""}`); +} + +// Or retry only the required plugins that are still missing. +const { plugins } = await session.rpc.plugins.list(); +const missing = plugins + .filter((plugin) => plugin.managedDesiredEnabled === true && plugin.installed === false) + .map((plugin) => `${plugin.name}@${plugin.marketplace}`); +await session.rpc.plugins.retryManaged({ plugins: missing }); +``` + +
+ +Runtimes that do not prepare managed plugins for the session reject the call. + ## Troubleshooting * **"no plugin.json or SKILL.md found in <dir>"** — the directory exists but doesn't qualify as a plugin. Add a `plugin.json` manifest at the root (or under `.github/`), or include a top-level `SKILL.md`. diff --git a/docs/features/session-persistence.md b/docs/features/session-persistence.md index 302844b347..d3826e4f2b 100644 --- a/docs/features/session-persistence.md +++ b/docs/features/session-persistence.md @@ -131,6 +131,34 @@ await session.SendAndWaitAsync(new MessageOptions { Prompt = "Analyze my codebas Later—minutes, hours, or even days—you can resume the session from where you left off. +### Remembered permissions with a session filesystem + +When you configure a custom `sessionFs` provider, runtime-owned durable permission choices use provider storage rather than the runtime host's configuration. The files live under the provider's `sessionStatePath` and retain their existing formats: + +* `permissions.json`: A top-level `locations` map retaining the existing `tool_approvals` and `allowed_directories` fields +* `settings.json`: Top-level `allowedUrls` for `approve-permanently` URL approvals and `sandbox` for saved sandbox preferences +* `config.json`: Top-level `trustedFolders` for remembered folder trust and `sandboxOnboardingShown` for the first-enable sandbox message + +Location keys are normalized provider directory paths, such as `/workspace`, without an added prefix. The runtime resolves them lexically using provider path conventions, without inspecting the host filesystem or discovering host Git repositories. An approval saved for `/workspace` is not automatically selected when the working directory is `/workspace/sub`. + +Provider-backed folder trust also uses lexical paths: a trusted folder covers itself and its descendants, not linked Git worktrees at other paths. Trust those worktree paths explicitly. + +There is no additional session-ID partition inside these files. Sessions that access the same backing files share remembered choices. Use separate provider namespaces when sessions or tenants must not share grants. With a namespace per session, permanent choices survive that session's resumes but do not automatically carry over to new sessions. To restore a session's persisted choices on resume, reconnect it to the same namespace. + +Consent updates are serialized per provider endpoint and file within one runtime process. Separate endpoints do not block each other. If different provider endpoints or runtime processes share backing files, the application must coordinate concurrent updates; the runtime cannot infer that those files are shared. + +These operations never import, update, or fall back to the host's `permissions-config.json`, `settings.json`, or `config.json`, including when provider reads or writes fail. Existing-format files supplied through the provider are read directly. + +As with host configuration, URL approvals are read only from `settings.json`. A legacy `allowedUrls` field in `config.json` is ignored; `config.json` remains the global-state file for remembered folder trust and the sandbox onboarding marker. + +Storage routing does not change permission lifetimes, URL denial precedence, or session policy behavior. Approve-once and approve-for-session do not become permanent grants. + +Saved sandbox preferences are restored on create and resume. Later explicit SDK `options.update` calls can change the live sandbox configuration without rewriting the saved preference. + +Without `sessionFs`, SDK applications in both `empty` and `copilot-cli` modes retain existing host-backed persistence. This includes the existing behavior where permanent URL consent saves host settings and grants access in the live session, but a fresh SDK session does not automatically load the host URL allowlist. + +See [Multi-tenancy and server deployments](../setup/multi-tenancy.md#sessionfs) for provider isolation responsibilities. + ### Transcript recovery The resume option `allowTranscriptRecovery` controls recovery when the runtime loads a @@ -283,7 +311,17 @@ When resuming a session, you can optionally reconfigure many settings. This is u With `model: "auto"`, the optional `capi.autoTier` setting selects an Auto routing preference: `efficiency`, `balance`, `intelligence`, or `fast`. In Python, use `capi={"auto_tier": "balance"}`. This setting applies to V2 Auto routing; V1 Auto requests are unchanged. -`fast` is an integrator-only latency preset, not a first-party GitHub Copilot product preference. The SDK does not decide Fast eligibility, inspect client identity, choose it as a default, or fall back to another tier when a runtime does not support it—an older runtime returns its native error unchanged. +When the runtime's default-off `DYNAMIC_AUTO_TIERS` feature is enabled, the session's `model.list` RPC also returns optional `auto` metadata from the provider's `/meta` endpoint. Use the enabled descriptors with `type: "auto"` to discover additional supported tier identifiers, their display names, descriptions, and ordering. The existing selection methods and JSON fields accept these identifiers. A discovery failure does not fall back to `/models`, and a successful response without `auto` metadata provides no selectable Auto tiers. + +Treat tier identifiers as extensible values, not a closed enumeration. Existing named values remain available; for additional identifiers, use strings in Node.js and Python, `AutoTier("premium-v2")` in Go, `new AutoTier("premium-v2")` in .NET, `AutoTier::Custom` in Rust, or `AutoTier.fromValue` in Java. The provider's `defaultTier` describes its default routing without creating a committed SDK preference. + +If a persisted tier is removed or disabled, resume preserves the explicit preference rather than replacing it with the provider's default. Select an available replacement before activating Auto routing. + +The CLI also preserves settings-derived preferences during startup, even when a tier is unavailable or dynamic discovery is disabled. You can then select a replacement in the model picker. Explicit SDK create and resume preferences are still validated before the operation succeeds, and Auto activation always validates availability against the resolved discovery mode. + +When a create or resume request includes `expAssignments`, the runtime installs those assignments before validating an explicit Auto preference. Validation, discovery, and activation use the same resolved feature decision. + +`fast` is an integrator-only latency preset, not a first-party GitHub Copilot product preference. Fast is not validated against the `/meta` tier catalog when configuring a session. The SDK does not decide Fast eligibility, inspect client identity, choose it as a default, or fall back to another tier when a runtime does not support it—an older runtime returns its native error unchanged. The runtime persists the selected tier, so applications do not need to resend it on every resume: @@ -309,6 +347,8 @@ if (result.status === "pending") { The runtime does not apply the preference immediately. It records the request and commits it only when a later user turn using the `auto` model successfully obtains a usable model from the provider. A `pending` status therefore confirms that the request was accepted, not that it took effect. Only the most recent request survives: a new request replaces any earlier one that no turn has claimed yet. +If an older request is still validating when a newer preference request or reset arrives, the older call fails with a superseded-request error instead of replacing the newer intent. + Watch for the outcome through these events: * `session.model_change` when the preference commits. diff --git a/docs/features/skills.md b/docs/features/skills.md index 30c5112dbd..df8d319928 100644 --- a/docs/features/skills.md +++ b/docs/features/skills.md @@ -336,6 +336,333 @@ The frontmatter fields: The markdown body contains the instructions that are injected into the session context when the skill is loaded. +## Skill providers (experimental) + +> [!NOTE] +> Skill providers are experimental. The API can change in future SDK releases. + +A skill provider serves skills from your application's own storage, such as a database in a multi-tenant service, instead of `SKILL.md` files on disk. Provider skills join the session's skill catalog alongside file-based skills. The model loads them on demand through the `skill` tool, and users can invoke them like any other skill. + +A provider implements two operations: + +* **List skills**: returns catalog metadata for every skill: a `name`, a `description`, and optionally `userInvocable`, `disableModelInvocation`, and `argumentHint`. The runtime calls it when it loads the session's skills. +* **Read skill**: returns the markdown for one skill, or a not-found result if the skill no longer exists. The runtime calls it only when the skill is loaded. + +Pass the provider when you create or resume a session: + +
+Node.js / TypeScript + +```typescript +import { approveAll, CopilotClient, type SkillProvider } from "@github/copilot-sdk"; + +const releaseSkills = new Map([ + [ + "release-notes", + { + description: "Writes release notes in the team's format.", + markdown: "Group changes by feature area and link each pull request.", + }, + ], +]); + +const skillProvider: SkillProvider = { + listSkills: () => + [...releaseSkills].map(([name, skill]) => ({ name, description: skill.description })), + readSkill: (name) => releaseSkills.get(name)?.markdown ?? null, +}; + +const client = new CopilotClient(); +const session = await client.createSession({ + onPermissionRequest: approveAll, + skillProvider, +}); +``` + +
+
+Python + +```python +from copilot import CopilotClient, SkillProviderDescriptor +from copilot.session import PermissionHandler + +RELEASE_SKILLS = { + "release-notes": ( + "Writes release notes in the team's format.", + "Group changes by feature area and link each pull request.", + ), +} + + +class ReleaseSkills: + async def list_skills(self) -> list[SkillProviderDescriptor]: + return [ + SkillProviderDescriptor(name=name, description=description) + for name, (description, _) in RELEASE_SKILLS.items() + ] + + async def read_skill(self, name: str) -> str | None: + skill = RELEASE_SKILLS.get(name) + return skill[1] if skill else None + + +async def main(): + client = CopilotClient() + await client.start() + session = await client.create_session( + on_permission_request=PermissionHandler.approve_all, + skill_provider=ReleaseSkills(), + ) +``` + +
+
+Go + +```go +package main + +import ( + "context" + "fmt" + "log" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/rpc" +) + +type releaseSkill struct { + description string + markdown string +} + +type releaseSkills map[string]releaseSkill + +func (s releaseSkills) ListSkills(ctx context.Context) ([]rpc.SkillProviderDescriptor, error) { + descriptors := make([]rpc.SkillProviderDescriptor, 0, len(s)) + for name, skill := range s { + descriptors = append(descriptors, rpc.SkillProviderDescriptor{Name: name, Description: skill.description}) + } + return descriptors, nil +} + +func (s releaseSkills) ReadSkill(ctx context.Context, name string) (string, error) { + skill, ok := s[name] + if !ok { + return "", fmt.Errorf("%w: %s", copilot.ErrSkillNotFound, name) + } + return skill.markdown, nil +} + +func main() { + ctx := context.Background() + client := copilot.NewClient(nil) + session, err := client.CreateSession(ctx, &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + SkillProvider: releaseSkills{ + "release-notes": { + description: "Writes release notes in the team's format.", + markdown: "Group changes by feature area and link each pull request.", + }, + }, + }) + if err != nil { + log.Fatal(err) + } + _ = session +} +``` + +
+
+.NET + +```csharp +#pragma warning disable GHCP001 // Skill providers are experimental. +using GitHub.Copilot; +using GitHub.Copilot.Rpc; + +public sealed class ReleaseSkills : ISkillProvider +{ + private readonly Dictionary _skills = new() + { + ["release-notes"] = ( + "Writes release notes in the team's format.", + "Group changes by feature area and link each pull request."), + }; + + public Task> ListSkillsAsync(CancellationToken cancellationToken) => + Task.FromResult>( + _skills.Select(skill => new SkillProviderDescriptor + { + Name = skill.Key, + Description = skill.Value.Description, + }).ToList()); + + public Task ReadSkillAsync(string name, CancellationToken cancellationToken) => + Task.FromResult(_skills.TryGetValue(name, out var skill) ? skill.Markdown : null); +} + +public static class SkillProviderExample +{ + public static async Task RunAsync(CopilotClient client) + { + await using var session = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + SkillProvider = new ReleaseSkills(), + }); + } +} +``` + +The .NET skill provider types raise the `GHCP001` experimental diagnostic. Suppress it with `#pragma warning disable GHCP001` or a project-level `GHCP001`. + +
+
+Java + +```java +import com.github.copilot.AllowCopilotExperimental; +import com.github.copilot.CopilotClient; +import com.github.copilot.CopilotSession; +import com.github.copilot.SkillProvider; +import com.github.copilot.SkillProviderDescriptor; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.SessionConfig; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +@AllowCopilotExperimental +class ReleaseSkills implements SkillProvider { + private final Map markdown = Map.of( + "release-notes", "Group changes by feature area and link each pull request."); + + @Override + public CompletableFuture> listSkills() { + return CompletableFuture.completedFuture(List.of(new SkillProviderDescriptor( + "release-notes", "Writes release notes in the team's format.", null, null, null))); + } + + @Override + public CompletableFuture readSkill(String name) { + return CompletableFuture.completedFuture(markdown.get(name)); + } +} + +@AllowCopilotExperimental +class SkillProviderExample { + static CopilotSession createSession(CopilotClient client) throws Exception { + return client.createSession(new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setSkillProvider(new ReleaseSkills())) + .get(); + } +} +``` + +The Java skill provider API is `@CopilotExperimental`, so the consuming class or method must opt in with `@AllowCopilotExperimental` (or compile with `-Acopilot.experimental.allowed=true`). See [Using experimental APIs](../../java/README.md#using-experimental-apis). + +
+
+Rust + + +```rust +use std::sync::Arc; + +use async_trait::async_trait; +use github_copilot_sdk::{Error, SessionConfig, SkillProvider, SkillProviderDescriptor}; + +struct ReleaseSkills; + +#[async_trait] +impl SkillProvider for ReleaseSkills { + async fn list_skills(&self) -> Result, Error> { + Ok(vec![SkillProviderDescriptor { + name: "release-notes".into(), + description: "Writes release notes in the team's format.".into(), + ..Default::default() + }]) + } + + async fn read_skill(&self, name: &str) -> Result, Error> { + Ok((name == "release-notes") + .then(|| "Group changes by feature area and link each pull request.".to_string())) + } +} + +let session = client + .create_session( + SessionConfig::default() + .approve_all_permissions() + .with_skill_provider(Arc::new(ReleaseSkills)), + ) + .await?; +``` + +
+ +### Provider skill content + +Read skill returns the skill's `SKILL.md` text. YAML frontmatter is optional: + +* Without frontmatter, the whole text is the skill body, and all metadata comes from the listed skill. +* With frontmatter, an omitted field inherits the listed value. A field that you include must match the listed value, or the skill fails to load. +* `allowed-tools` is read only from frontmatter. +* If the first line of the text is `---`, the runtime parses it as frontmatter, so don't start a body-only skill with a Markdown thematic break. + +Provider skills are text-only. They have no base directory, so they can't reference bundled scripts, templates, or other files. `skills.list` reports them with the source `sdk` and an empty `path`. + +### Serving existing SKILL.md files + +To serve `SKILL.md` files that you already have, such as files stored in a database, parse each file's YAML frontmatter once to build its catalog entry, then return the file unchanged from read skill: + +* Map `name` and `description` to the listed skill's `name` and `description`. Both are required in the catalog; the runtime doesn't fall back to a folder name or the skill body. +* Map `user-invocable`, `disable-model-invocation`, and `argument-hint` to the matching optional fields when the frontmatter sets them. +* Return the original text, frontmatter included, from read skill. Because the listed values came from the same frontmatter, they match, and the runtime still reads `allowed-tools` from it. + +### Trusting provider content + +Treat provider skills like skill directories: their content is trusted input. The model follows a skill's instructions, and the frontmatter controls how the skill is invoked. `allowed-tools` doesn't grant permissions in SDK sessions. The runtime reports it in the `allowedTools` field of the `skill.invoked` event, and your permission handler still decides every tool request. + +Don't serve text that end users or other tenants can edit unless you would let them author a skill file. If you build skills from user input, generate the frontmatter yourself instead of passing user-supplied frontmatter through. + +### Provider limits and errors + +The runtime validates the provider's catalog and content: + +* Names must match `^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$` and be unique, ignoring case. +* A catalog can contain at most 1,024 skills and 1 MiB of metadata. Each description and argument hint can be at most 1,024 characters. +* Each skill's markdown can be at most 1 MiB of UTF-8. +* Each call must complete within 30 seconds. When the runtime stops waiting for a call, because it timed out, the session ended, or a resume replaced the provider, it cancels the request and ignores any later result. Each SDK passes that cancellation to your provider in its usual way: + + | SDK | Cancellation signal | + |---|---| + | Node.js | The `signal` (`AbortSignal`) in the options passed to `listSkills` and `readSkill` is aborted. | + | Python | The provider's `asyncio` task is cancelled with `asyncio.CancelledError`. Synchronous providers run to completion. | + | Go | The call's `context.Context` is cancelled. | + | .NET | The call's `CancellationToken` is cancelled. | + | Java | The returned `CompletableFuture` is cancelled with `cancel(true)`, which doesn't interrupt running work. | + | Rust | The provider future is dropped. | + + These signals cover calls that the runtime cancels. When the connection closes or the client is force-stopped, the .NET, Java, and Rust SDKs also cancel calls that are still running. The Node.js, Python, and Go SDKs don't: a running call continues until it returns, and the SDK discards its result. + +If listing fails or returns an invalid catalog, the runtime reports the problem in the `errors` list returned by `skills.reload`. A failed reload keeps the provider skills from the last successful list. If reading a skill fails, the `skill` tool reports a generic load failure to the model. The SDK never forwards the text of errors your provider throws or returns, so that text can't leak into the conversation. + +### Provider lifecycle + +A provider is bound to one session: + +* A provider enables skills unless you set `enableSkills` to `false`, which keeps the provider bound but never called. In `mode: "empty"`, skills stay disabled until you set `enableSkills` to `true`. +* The provider is never persisted. Pass it again when you resume a session. Resuming without a provider removes it from the session. Extensions that join the session don't change it. +* A provider skill with the same name as a file-based skill replaces the file-based skill, and `skills.reload` reports a warning. +* Sub-agents use their parent session's provider. +* The runtime can call the provider concurrently, for example from the `skill` tool, user invocations, and custom agents that preload skills, so both operations must be safe for concurrent use. +* Cloud sessions don't support skill providers. The SDK rejects the configuration before it creates the session. + ## Configuration options ### SessionConfig skill fields @@ -344,12 +671,18 @@ The markdown body contains the instructions that are injected into the session c |----------|-------|------|-------------| | Node.js | `skillDirectories` | `string[]` | Directories to load skills from | | Node.js | `disabledSkills` | `string[]` | Skills to disable | +| Node.js | `skillProvider` | `SkillProvider` | Experimental provider for SDK-supplied skills | | Python | `skill_directories` | `list[str]` | Directories to load skills from | | Python | `disabled_skills` | `list[str]` | Skills to disable | +| Python | `skill_provider` | `SkillProvider` | Experimental provider for SDK-supplied skills | | Go | `SkillDirectories` | `[]string` | Directories to load skills from | | Go | `DisabledSkills` | `[]string` | Skills to disable | +| Go | `SkillProvider` | `SkillProvider` | Experimental provider for SDK-supplied skills | | .NET | `SkillDirectories` | `List` | Directories to load skills from | | .NET | `DisabledSkills` | `List` | Skills to disable | +| .NET | `SkillProvider` | `ISkillProvider` | Experimental provider for SDK-supplied skills | +| Java | `setSkillProvider` | `SkillProvider` | Experimental provider for SDK-supplied skills | +| Rust | `with_skill_provider` | `Arc` | Experimental provider for SDK-supplied skills | ### Built-in skills and `mode: "empty"` diff --git a/docs/features/streaming-events.md b/docs/features/streaming-events.md index 0292f98e00..b20339042a 100644 --- a/docs/features/streaming-events.md +++ b/docs/features/streaming-events.md @@ -532,12 +532,31 @@ Emitted when a tool begins executing. ### `tool.execution_partial_result` -Ephemeral. Incremental output from a running tool (e.g., streaming bash output). +Deprecated. Use `tool.shell_output` for live terminal output. This ephemeral event remains emitted alongside the replacement during migration; subscribe to only one representation to avoid displaying the same output twice. | Data Field | Type | Required | Description | |------------|------|----------|-------------| | `toolCallId` | `string` | ✅ | Matches the corresponding `tool.execution_start` | -| `partialOutput` | `string` | ✅ | Incremental output chunk | +| `partialOutput` | `string` | ✅ | Bounded, cumulative replacement snapshot with stdout and stderr merged; not an append-only chunk | + +### `tool.shell_output` + +Ephemeral. Append-only output from an attached terminal command or a user-requested terminal command. Each event contains newly observed, secret-filtered text, without the separators or markers used to format final tool results for the model. + +| Data Field | Type | Required | Description | +|------------|------|----------|-------------| +| `toolCallId` | `string` | ✅ | Identifies the originating tool call | +| `text` | `string` | ✅ | New text to append, not a replacement snapshot | +| `stream` | `"stdout" \| "stderr" \| "terminal"` | | Output source; omitted means `"stdout"`. `"terminal"` identifies inherently merged PTY output, whose stdout/stderr origin cannot be recovered | +| `sequence` | `number` | ✅ | Starts at zero and increases across all streams for this tool call, in observed publication order | + +Accumulate `text` separately by `toolCallId` and `stream`, or join the chunks in sequence order for a combined display. A sequence number identifies duplicates and missing chunks; it does not establish exact ordering between independent operating-system writes. A late subscriber can receive a first sequence greater than zero. + +These events are live-only: they are not persisted or replayed after resume. Final `tool.execution_complete` results, model-facing formatting, and detached command capture are unchanged. Text can contain terminal control sequences; renderers must apply their normal terminal-text handling. + +An attached terminal command can keep running after `tool.execution_complete` when it runs asynchronously or continues beyond `initial_wait`. Output received while it continues uses the original `toolCallId` and continues the same `sequence`, rather than restarting at zero. Consumers that display only active calls must ignore chunks received after completion instead of recreating completed-call state. + +Secret filtering operates on each decoded chunk. The runtime does not artificially split lines, but operating-system reads can divide a line or secret across chunks; cross-chunk secret reconstruction is not provided. ### `tool.execution_progress` @@ -715,7 +734,7 @@ The `permissionRequest` is a discriminated union on `kind`: | `kind` | Key Fields | Description | |--------|------------|-------------| | `"shell"` | `fullCommandText`, `intention`, `commands[]`, `possiblePaths[]` | Execute a shell command | -| `"write"` | `fileName`, `diff`, `intention`, `newFileContents?` | Write/modify a file | +| `"write"` | `fileName`, `diff`, `intention`, `newFileContents?`, `fileEdits?` | Write/modify a file | | `"read"` | `path`, `intention` | Read a file or directory | | `"mcp"` | `serverName`, `toolName`, `toolTitle`, `args?`, `readOnly` | Call an MCP tool | | `"url"` | `url`, `intention` | Fetch a URL | @@ -724,6 +743,10 @@ The `permissionRequest` is a discriminated union on `kind`: All `kind` variants also include an optional `toolCallId` linking back to the tool call that triggered the request. +Write requests can also include `fileEdits` in both `permissionRequest` and `promptRequest`. Each entry contains an optional `before` and `after` snapshot, each with an absolute `path` in the session filesystem namespace and complete UTF-8 `content`. A missing `before` means creation; a missing `after` means deletion. An empty `content` string represents an empty file, not an unavailable preview. A move can have different paths on its two sides. When a move overwrites an existing destination, separate entries describe the source deletion and destination replacement. + +These snapshots describe the planned operation, not a later read of the live filesystem. The runtime omits `fileEdits` when it cannot provide a complete text preview, including requests made before reading a sandbox-protected file, non-UTF-8 contents, or an unreadable or non-regular move destination, such as a named pipe. Do not infer a deletion from an empty legacy `newFileContents` value. The existing fields and approval choices remain available to consumers that do not use previews. + ### `permission.completed` A permission request was resolved. @@ -837,7 +860,7 @@ A skill was activated for the current conversation. | `name` | `string` | ✅ | Skill name | | `path` | `string` | ✅ | File path to the SKILL.md definition | | `content` | `string` | ✅ | Full skill content injected into the conversation | -| `allowedTools` | `string[]` | | Tools auto-approved while this skill is active | +| `allowedTools` | `string[]` | | Tools listed in the skill's `allowed-tools` frontmatter. The SDK doesn't approve them automatically; your permission handler still decides. The Copilot CLI auto-approves them while the skill is active. | | `pluginName` | `string` | | Plugin the skill originated from | | `pluginVersion` | `string` | | Plugin version | @@ -970,7 +993,7 @@ assistant.turn_start → Turn begins │ ├── permission.requested → Needs user approval │ ├── permission.completed → Approval result │ ├── tool.execution_start → Tool begins -│ ├── tool.execution_partial_result → Streaming tool output (ephemeral, repeated) +│ ├── tool.shell_output → Stream-tagged terminal output (ephemeral, repeated) │ ├── tool.execution_progress → Progress updates (ephemeral, repeated) │ ├── tool.execution_complete → Tool finished │ │ @@ -998,6 +1021,7 @@ This table lists key `data` payload fields. Common envelope fields are documente | `tool.user_requested` | | Tool | `toolCallId`, `toolName`, `arguments?` | | `tool.execution_start` | | Tool | `toolCallId`, `toolName`, `arguments?`, `mcpServerName?` | | `tool.execution_partial_result` | ✅ | Tool | `toolCallId`, `partialOutput` | +| `tool.shell_output` | ✅ | Tool | `toolCallId`, `text`, `stream?`, `sequence` | | `tool.execution_progress` | ✅ | Tool | `toolCallId`, `progressMessage` | | `tool.execution_complete` | | Tool | `toolCallId`, `success`, `result?`, `error?` | | `session.idle` | ✅ | Session | `aborted?` | diff --git a/docs/features/usage-and-billing.md b/docs/features/usage-and-billing.md index ec662b6850..c412cb35c2 100644 --- a/docs/features/usage-and-billing.md +++ b/docs/features/usage-and-billing.md @@ -22,9 +22,21 @@ The table below maps each signal to the API that exposes it. | Accumulated AI credit and token totals | `session.usage.getMetrics` | Session | RPC | | Per-model AI credit pricing | `models.list` | Server | RPC | | Account quota and premium interactions | `account.getQuota` | Server | RPC | +| Session-owned provider quota observations | `session.quota.get` and `session.quota.refresh` | Session | RPC | +| Provider admission-state updates | `session.quota_observation` | Session | Event | + +`session.quota.get` reads local runtime state; `session.quota.refresh` refreshes the provider catalogs and quota sources. Both return `providerQuotas`, keyed by provider identity, quota ID, and observation kind. The runtime keeps one observation store. The legacy `snapshots` field is a derived compatibility view of authoritative budgets, not a second source of quota state. + +Provider cardinality is an emergent property of actual usage and observations supplied to the seam. This API neither knows nor enforces how many providers a user may use: it records and projects zero, one, or many entries identically. Producers supply stable provider/account-scoped references; usage adds model identity, and quota adds quota identity and observation kind. `get()` returns the full known collection, so callers can select an exact key, a subset, or all entries without provider-specific methods. Acquisition uses the sources supplied by the upstream account/model layer; it does not manufacture a union or impose a selected-provider filter on reads. + +Check `observationKind`, `quantityKind`, and `acquisitionStatus` before interpreting a reading. An `authoritative_budget` can supply progress and reset metadata. An `advisory_balance` reports independent signed 64-bit quantities without promising a percentage, reset window, or post-call balance. An `admission_state` reports permission/capacity at request admission and carries no quantities; it does not replace an account balance. Preserve missing values and explicit nulls rather than converting them to zero. A failed or unavailable acquisition is not quota exhaustion. + +For Microsoft 365 Copilot, access and capacity are service verdicts, while quantities are advisory. For example, `allowed`/`available` with both quantities explicitly zero means **0 / 0 AIC reported**, not 100% consumed or inconsistent state. A missing entitlement is different from a zero entitlement. The `-1` entitlement sentinel means unlimited entitlement without changing the reported capacity state. Inference headers can reflect cached admission state; neither those headers nor an on-demand quota refresh establish the charge for the preceding call. + +Usage totals belong to a session, not the active account. In the CLI, switching away from a non-empty Microsoft 365 Copilot session requires starting a new session. The previous session retains its usage and history for resume; the new session starts its own totals. > [!NOTE] -> `session.usage.getMetrics`, `session.metadata.contextInfo`, and `session.metadata.recomputeContextTokens` are marked experimental in the generated RPC surface. In .NET they raise the `GHCP001` experimental diagnostic, which you suppress with `#pragma warning disable GHCP001` or a project-level `GHCP001`. Pin both the SDK and the Copilot CLI runtime if your application depends on them. +> `session.quota`, `session.usage.getMetrics`, `session.metadata.contextInfo`, and `session.metadata.recomputeContextTokens` are marked experimental in the generated RPC surface. In .NET they raise the `GHCP001` experimental diagnostic, which you suppress with `#pragma warning disable GHCP001` or a project-level `GHCP001`. Pin both the SDK and the Copilot CLI runtime if your application depends on them. The field tables below list only the fields used in the examples on this page. The complete, always-current field reference is the generated SDK types plus [Streaming events](./streaming-events.md), which is regenerated from the CLI schema on every dependency bump. Treat those as the source of truth and this page as a task-oriented guide. diff --git a/docs/getting-started.md b/docs/getting-started.md index 5512da8531..b6172d6fc8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1843,6 +1843,14 @@ Copilot decides when to call your tool based on the user's question. When it doe 1. The result is sent back to Copilot 1. Copilot incorporates the result into its response +### Updating external tools in a running session + +The existing low-level `session.tools.set` request replaces the definitions owned by that SDK connection. Send the complete desired list (or an empty list to remove them), rather than a patch. The updated definitions apply to the next model request that reads them. During a tool round, a change processed before the post-tool refresh is visible on the next model call; a concurrent update processed after that refresh takes effect on a later request. It does **not** update the high-level SDK tool-handler map: register and manage handlers separately, or handle `external_tool.requested` and complete it with `session.tools.handlePendingToolCall`. + +On direct Anthropic API connections to `https://api.anthropic.com` using a model marked for Anthropic tool updates in the runtime's model catalog (currently Claude Fable 5 or 5.1, or Claude Opus 4.8, 5, or 5.5), the runtime can keep the original tool prefix and represent eligible between-turn SDK changes in provider-native conversation items to preserve a cacheable prefix. On the CAPI WebSocket Responses route for a model marked for Responses tool updates in that catalog (currently GPT-5.4 and 5.4 Mini, GPT-5.5, GPT-5.6 Luna, Sol, Terra, and Sol-fast, GPT-6 Astra, Sol, and Luna, and GPT-6.1 Sol), eligible append-only additions can use positioned `additional_tools` items. Eligible removals retain historical definitions and restrict callable tools with `tool_choice: { type: "allowed_tools", ... }`, or `"none"` when no tools remain. This removal optimization requires every callable survivor to be present in the original top-level tool list: CAPI rejects allowlist references to inline-added definitions when full history is resent. Removing an inline-added tool can preserve its historical position, but retaining a callable inline-added tool while removing another tool falls back to the complete current list. Schema replacements and unsupported tool-choice or provider-native tool configurations also use that fallback. + +Model support is an explicit catalog opt-in, independent of support for reasoning configuration updates, and other models should be enabled only after their route is verified. If historical changes no longer fit the prompt budget, or an optimized request receives a recognized pre-output provider rejection, the runtime returns to the current complete tool list. Anthropic through CAPI, unannotated OpenAI Responses models, other provider endpoints, mid-turn changes, and history rewrites use the current complete tool list. Removing every callable tool selects the runtime's existing single-shot HTTP path, which also uses the complete current list rather than this optimization. Successful compaction starts a new prefix with the tools currently provided; resuming in a new process does not replay previous tool changes. A stable request shape does not guarantee cached tokens or lower cost. + ## What's next? Now that you've got the basics, here are more powerful features to explore: diff --git a/docs/observability/opentelemetry.md b/docs/observability/opentelemetry.md index b3932ce66a..2078b0cfe5 100644 --- a/docs/observability/opentelemetry.md +++ b/docs/observability/opentelemetry.md @@ -112,6 +112,53 @@ let client = Client::start(ClientOptions::new() The OTLP protocol field configures the CLI's `"otlp-http"` exporter for all signals. Leave it unset to use the CLI default, or set it to `"http/protobuf"` to export protobuf over HTTP. +### Skill and command telemetry + +The runtime follows the [GenAI skill semantic conventions](https://github.com/open-telemetry/semantic-conventions-genai/commit/771e3210f7518694666834f7243573b8018c7a4f) +by refining existing `execute_tool` spans, not adding a separate skill +operation. A loader is named `execute_tool skill {skill}`. An unambiguously +identified resource or script is named +`execute_tool {tool} {skill} {resource}`, with a skill-relative resource name. +Other command spans append the actual known launcher name. + +* `gen_ai.skill.name` and `gen_ai.skill.resource.name`: Available independently of content capture +* `gen_ai.skill.description` and `gen_ai.skill.source.uri`: Require content capture through `TelemetryConfig` +* `process.executable.name`: The directly launched executable, such as the shell launcher, not a guessed command token +* `process.executable.path`: An absolute known path; requires content capture +* `process.exit.code`: An integer, including zero, when a command-executing tool reports an exit; unavailable exits and pre-spawn failures are omitted + +Poll tools do not inherit process attributes from the command they read. +Sandboxed executable identities are omitted when the sandbox backend does +not expose them. A nonzero exit does not imply that the tool itself failed. +Skill definitions and directory listings are not named skill resources. +Preloaded skills produce invocation events without a fabricated tool span. +Repeated preparation of the same selected-agent skill context emits one receipt; +changed content or provenance emits another. Child sessions report their own +preloads independently. Detached command spans identify the directly spawned +Unix `sh` supervisor or selected Windows PowerShell executable. + +Custom tools that override built-in names do not inherit native skill or process +attributes. Visible, enabled skills still own resources when model invocation +is disabled. Script attribution uses the post-hook command and the selected +shell's directory; it is omitted when startup scripts or profiles make the +execution context uncertain. + +While managed telemetry settings are unresolved, pending tracking buffers only +capture-independent tool facts. Descriptions, source URIs, and executable paths +observed before the policy resolves are omitted, even if capture is later enabled. +The final policy governs replay; disabled or cancelled tracking discards the buffer. + +These are breaking telemetry-name changes: use `gen_ai.skill.name` instead +of `github.copilot.tool.parameters.skill_name` or +`github.copilot.skill.name`, and use `gen_ai.skill.source.uri` instead of +`github.copilot.skill.path`. Local source values are file URIs, not bare paths. +The `github.copilot.skill.invoked` event and its content, source-category, +trigger, and plugin fields remain Copilot-specific. Skill content retains its +existing capture gate. + +The skill attributes and span refinements have Development stability; the +process attributes have Release Candidate stability. + ### Trace context propagation > **Most users don't need this.** The `TelemetryConfig` above is all you need to collect traces from the CLI. The trace context propagation described in this section is an **advanced feature** for applications that create their own OpenTelemetry spans and want them to appear in the **same distributed trace** as the CLI's spans. diff --git a/docs/runtime-supervised-host.md b/docs/runtime-supervised-host.md index 427fe39daf..c0999bddbb 100644 --- a/docs/runtime-supervised-host.md +++ b/docs/runtime-supervised-host.md @@ -106,14 +106,23 @@ Startup fails if a requested transport cannot become ready, rather than silently downgrading to local-only hosting. Select transports at startup; dispose and recreate the host to change them. +`githubEnvironment.requireConnectionBinding` defaults to `true`: sealed +authentication must bind the current handshake challenge, a fresh nonce and +timestamp. Explicit `false` selects compatibility with clients sending unbound +encrypted tokens. Relay encryption and resource authorization stay mandatory, +but an unbound sealed token is replayable. This is the same transitional policy +supported by `copilotd`, not a switch to plaintext authentication. + GitHub-only hosting does not open a local listener. Its host handle has an environment ID but no local URL or connection token. `environmentId` is absent for local-only hosting. Disposal ends transport and registration activity without deleting the saved MC environment record or application-owned sessions. Registering an environment does not publish every application session. Use -`publishSession` for existing resident sessions; factory callbacks and durable -catalog behavior are unchanged. Environment management is independent of a +`publishSession` for an existing resident session, and `listSessions` to read +the host's complete advertised catalog of live and dormant sessions. Listing +does not start a host, publish a session, or paginate results. Factory callbacks +and durable catalog behavior are unchanged. Environment management is independent of a running host and available only through generated `rpc.environments.list`, `rpc.environments.get`, and `rpc.environments.delete` operations, using each language's naming conventions. @@ -378,7 +387,8 @@ configuration before calling these methods, just as in the creation examples. Python and .NET handles support asynchronous context management; Java handles support try-with-resources. Go callers explicitly invoke `Dispose(ctx)`. -Only durable catalog entries marked as application-owned invoke the resume +Durable catalog entries marked as application-owned, including published +application sessions, invoke the resume factory. If its resume callback is missing, restoration fails rather than silently falling back to host-owned creation. Published resident sessions attach directly and do not invoke either factory. This callback does not provide arbitrary adoption or reconfiguration @@ -398,11 +408,33 @@ and `sessionUri`. Publication does not call the factory, copy history, replace the native session, or transfer ownership. Its metadata and workspace come from the resident session, not from an application-supplied configuration. -Published sessions are discoverable only for this listener's lifetime. They -are not imported into its durable catalog. Stopping the listener detaches its -participation without deleting the original session or transcript. A missing -or replaced resident session cannot be silently restored from disk by the -listener. +Publication records the session in the durable, compute-scoped host catalog. +Stopping the listener detaches its participation without deleting the catalog +entry, original session, or transcript. A later host using the same compute +identity can discover it as a dormant session. Restoring an application-owned +session requires the owning application's `resumeSession` callback to configure +its tools, hooks, and handlers; the host does not silently substitute host-owned +session construction. A currently attached resident session still attaches +directly without invoking a factory. The SDK does not expose an unpublish operation. + +### Listing host sessions + +Call the owner-bound host handle to read every live or dormant session currently +advertised by its host. The runtime returns the complete catalog in one response; +the method does not start a host or publish sessions. The host ID is supplied +by the handle, so the call stays on the original owning connection. +Each method returns a `HostListSessionsResult` with a `sessions` collection, +not the collection directly. In Node.js, use +`const { sessions } = await host.listSessions()`. + +| SDK | Method | +| --- | --- | +| Node.js | `host.listSessions()` | +| Python | `await host.list_sessions()` | +| Go | `host.ListSessions(ctx)` | +| .NET | `host.ListSessionsAsync(cancellationToken)` | +| Java | `host.listSessions()` | +| Rust | `host.list_sessions().await` | ### CLI hosting commands @@ -417,14 +449,11 @@ Sharing also shows the session URI. Use an AHP 0.9 client with the connection token and ordinary GitHub resource authentication; the connection token alone does not bypass resource authorization. -`/ahp status` shows connection information. `/ahp stop` stops the **entire -listener and all its shares**. `/remote unshare` removes the foreground session. -The pinned host has no per-session unregister operation, so unsharing drains -and restarts the listener at the same endpoint with the same connection token -before republishing other shares. Other clients must reconnect; local sessions -and their identities remain unchanged. Exiting the owning CLI stops the -listener as well. There is one listener per CLI/effective catalog, not one -listener per shared session. +`/ahp status` shows connection information. `/ahp stop` stops the entire +listener and its active participation, not its durable catalog. Other clients +must reconnect after hosting restarts; local sessions and their identities +remain unchanged. Exiting the owning CLI stops the listener as well. Hosting +lifetime is separate from durable publication. `--ahp-host [--listen host:port] [--workspace directory]` serves this same backend in the CLI process, using normal SDK session construction, managed policy, @@ -440,19 +469,36 @@ Outbound relay, host-picker and explicitly configured external-daemon controls r ## Durable catalog and single host owner -The runtime passes its actual resolved data directory to the host library; the single AHP -catalog lives at `/ahp/sessions`. It follows the same +The runtime passes its actual resolved data directory and compute identity to +the host library. Durable catalogs are scoped to both the effective Copilot +home and compute identity. The effective home follows the same default `~/.copilot`, `COPILOT_HOME`, and SDK `baseDirectory` resolution as that -runtime. The catalog contains sessions previously created through AHP, not all -SDK/CLI sessions. Listener disposal, owner disconnect, and restart retain it. +runtime. Each catalog contains sessions previously created through AHP and +explicitly published resident sessions, not all SDK/CLI sessions. +Listener disposal, owner disconnect, and restart retain it. A replacement listener can list and resume these sessions after authenticating. +Supply top-level `AhpHostOptions.computeId` to keep the same catalog when switching +between local and Mission Control hosting. If omitted for a local-only start, +the runtime persists a stable identity in its settings; the SDK never generates +one. Mission Control's `githubEnvironment.computeId` remains required. When both +compute IDs are supplied, they must agree or startup fails. + +| SDK | Top-level compute identity | +| --- | --- | +| Node.js | `computeId` | +| Python | `compute_id` | +| Go | `ComputeID` | +| .NET | `ComputeId` | +| Java | `setComputeId(...)` | +| Rust | `with_compute_id(...)` | + Only one AHP server may own a catalog at a time. A second start for the -same location fails, including from another runtime. Ordinary runtimes, SDK +same home and compute identity fails, including from another runtime. Ordinary runtimes, SDK clients, and sessions do not acquire this lock and remain usable. The lock is kernel-managed, non-blocking, held until shutdown writes finish, and released -even after forced process termination. Different effective homes have separate -catalogs. This does not change standalone `copilotd` defaults or concurrency +even after forced process termination. Different effective homes or compute +identities have separate catalogs. This does not change standalone `copilotd` defaults or concurrency behavior, and does not add standalone/in-process shared-writer support. ## Current GHES shell limitation diff --git a/docs/setup/multi-tenancy.md b/docs/setup/multi-tenancy.md index ebbafe4fb0..7fe893a070 100644 --- a/docs/setup/multi-tenancy.md +++ b/docs/setup/multi-tenancy.md @@ -334,6 +334,24 @@ const client = new CopilotClient({ For languages that expose a provider callback, configure `sessionFs` at the client level and provide a per-session filesystem handler when creating or resuming a session. See [Session Persistence](../features/session-persistence.md) for persistence concepts and storage trade-offs. +Runtime-owned durable permission choices also use the session filesystem. Under the provider's `sessionStatePath`, `permissions.json` retains its top-level `locations` map with `tool_approvals` and `allowed_directories`; `settings.json` stores top-level `allowedUrls` and `sandbox`; and `config.json` stores top-level `trustedFolders`. + +URL approvals are read only from `settings.json`; legacy user-setting fields such as `allowedUrls` in `config.json` are ignored, matching host behavior. + +Location keys are normalized provider directory paths, with no added prefix. Resolution follows the provider's path conventions without inspecting the host filesystem or discovering host Git repositories. `/workspace` and `/workspace/sub` select different location entries. + +The runtime does not add session-ID partitions to these files or change their sharing granularity. Sessions accessing the same files in the same provider namespace share remembered grants. Your application must use distinct namespaces for tenants or sessions that require isolation, authorize access to each namespace, and reconnect resumed sessions to their original namespace. + +With a separate namespace per session, permanent grants survive that session's resumes rather than becoming tenant-wide grants for new sessions. Sharing grants across sessions requires sharing the consent files while keeping other session state separate. The runtime serializes consent updates only within the same provider endpoint and process; applications that share backing files across endpoints or processes must coordinate concurrent updates. + +The runtime blocks direct provider-backed file writes to the lexical approval-metadata paths as defense in depth, not as a tamper-proof storage boundary. Providers must not expose writable aliases (such as symlinks or Windows 8.3 short names) to approval metadata through agent file tools. Shell tools do not run through SessionFS, and their commands are not checked against its virtual metadata paths. If your application separately exposes the provider's backing storage to shell execution or other arbitrary code, your host must protect that storage. + +Session filesystem approval operations never import or fall back to remembered permissions from the host's `permissions-config.json`, URL or sandbox settings from `settings.json`, or folder trust from `config.json`. They do not update those host files, even when the provider has no approvals or reports an error. A provider error is not permission to use host consent. URL deny rules remain authoritative, and folder trust uses provider paths without host Git discovery. + +Approve-once, approve-for-session, rejections, and session policy changes retain their existing lifetimes and event-replay behavior. Storage routing does not turn them into new permanent grants. Protect session transcripts, including durable permission events, with the same tenant isolation as approval metadata. + +This boundary applies only to sessions using a session filesystem provider. All SDK applications without `sessionFs`, in both `empty` and `copilot-cli` modes, and ordinary CLI sessions retain existing host-backed persistence for remembered locations, permanent URL approvals, folder trust, and sandbox settings. + Verified public SDK surfaces: | Language | Client-level config | Per-session provider | diff --git a/docs/troubleshooting/compatibility.md b/docs/troubleshooting/compatibility.md index da8bf0daae..6054316f50 100644 --- a/docs/troubleshooting/compatibility.md +++ b/docs/troubleshooting/compatibility.md @@ -33,6 +33,7 @@ The Copilot SDK communicates with the CLI via JSON-RPC protocol. Features must b | Abort | `abort()` | Cancel in-flight request | | **Tools** | | | | Register custom tools | `registerTools()` | Full JSON Schema support | +| Replace custom tools (mid-session) | `session.setTools()` | Experimental; see [Changing tools during a session](../features/changing-tools.md) | | Tool permission control | `onPreToolUse` hook | Allow/deny/ask | | Tool result modification | `onPostToolUse` hook | Transform results | | Available/excluded tools | `availableTools`, `excludedTools` config | Filter tools | diff --git a/dotnet/README.md b/dotnet/README.md index e91656bf5a..84f5b650f1 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -44,6 +44,12 @@ For GitHub Mission Control, set instead, or configure both transports. GitHub environment name and compute ID are required; there is no implicit local listener. The host's `Url`, `Token`, and `Pid` are nullable; `EnvironmentId` contains the GitHub environment ID when configured. +Call `host.ListSessionsAsync()` to read the complete advertised catalog of live +and dormant sessions. +Set top-level `ComputeId` to reuse the durable catalog across local and Mission +Control hosting; it must agree with `GitHubEnvironment.ComputeId` when both are +supplied. The runtime persists an omitted local-only identity. Publication +survives host shutdown; dormant application sessions require your resume callback. Environment list/get/delete operations are available only through the generated RPC API. See [runtime-supervised AHP hosting](../docs/runtime-supervised-host.md) for creation and resume callbacks, resident-session publication, ownership, and shared-snapshot E2Es. @@ -110,6 +116,7 @@ new CopilotClient(CopilotClientOptions? options = null) - `GitHubToken` - GitHub token for authentication. When provided, takes priority over other auth methods. - `UseLoggedInUser` - Whether to use logged-in user for authentication (default: true, but false when `GitHubToken` is provided). Cannot be used with `RuntimeConnection.ForUri(...)`. - `Telemetry` - OpenTelemetry configuration for the runtime process. Providing this enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below. +- `SessionFs` - Custom session filesystem provider configuration. To serve provider-only images to the `view` tool, set `Capabilities.Binary` to `true` and return a provider implementing `ISessionFsBinaryProvider` (`ReadFileBytesAsync` and `WriteFileBytesAsync`) from `SessionConfig.CreateSessionFsProvider`. Image reads do not fall back to local files. Binary reads and writes are limited to 50,330,880 raw bytes (approximately 48 MiB); larger results return a filesystem error before encoding or decoding. - `InstallationConfirmationHandler` - Experimental connection-global human review for `installations.confirm`. Receives the typed request and one cancellation token that is cancelled when the request is retired or the connection closes, and returns an explicit decision. Does not enable installation capabilities. #### RuntimeConnection @@ -177,6 +184,9 @@ Start the CLI server and establish connection. ##### `StopAsync(): Task` Stop the server and close all sessions. Throws if errors are encountered during cleanup. +Closing a session stops subscribed event delivery, drops queued events, and releases +subscriptions without waiting for handlers already admitted for dispatch. Pending `SendAndWaitAsync` +operations fail with `IOException` if the session closes before their final response arrives. For an owned stdio runtime, graceful shutdown closes stdin and waits up to 10 seconds for host cleanup, including telemetry export. This cleanup is best-effort: if the wait times out, the process is terminated and that timeout alone is not reported as a cleanup @@ -185,6 +195,20 @@ error. A successful return does not guarantee that all telemetry was exported. ##### `ForceStopAsync(): Task` Force stop the CLI server without graceful cleanup. Use when `StopAsync()` takes too long. +Stops session event dispatch and releases queued events and subscriptions. A handler already +admitted for dispatch may finish; shutdown does not wait for it. Pending `SendAndWaitAsync` operations fail +with `IOException` if the session closes before their final response arrives. +The SDK releases its references to later handlers' captured objects even if an admitted handler remains blocked. +Local retirement also releases tool, command, hook, auth/input, provider, canvas, +and transform registrations, without changing caller-owned configuration or joining +callbacks already admitted. Registration and callback lookup share a retirement fence; +late accepted tool replacements cannot restore handlers. Ordinary disposal retains +hook callbacks through the detach response and performs local retirement in `finally`. +Both stop methods close session admission before taking the session snapshot. Concurrent +create/resume setup on that connection is rejected and retired locally; restarting the +client opens admission only for the new connection. +Cleanup of a rejected cloud creation stays on its original connection and cannot +restart the client during shutdown. ##### `CreateSessionAsync(SessionConfig? config = null): Task` @@ -206,6 +230,7 @@ Create a new conversation session. - `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. - `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`. @@ -224,6 +249,7 @@ Resume an existing session. Returns the session with `WorkspacePath` populated i - `OnPermissionRequest` - Optional handler called before each tool execution to approve or deny it. See [Permission Handling](#permission-handling) section. - `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. - `AllowTranscriptRecovery` - Repairs a damaged transcript when true. The default is true in all modes; set false to reject recovery. `session.TranscriptRecovery` contains @@ -253,6 +279,62 @@ await using var session = await client.CreateSessionAsync(new SessionConfig Initial acquisition runs during session creation or resume. Cancellation, provider errors, and invalid token responses reject that operation instead of falling back to ambient authentication. Idle sessions refresh only before their next credential-consuming operation; there is no background refresh timer. +##### Skill providers (experimental) + +Set `SessionConfig.SkillProvider` or `ResumeSessionConfig.SkillProvider` to +serve session-scoped skills from your application. The provider is not persisted: +pass it again on every resume, because resuming without one unbinds it. Skill +providers are only supported for local sessions; `CreateSessionAsync` throws if +`Cloud` and `SkillProvider` are both set. The skill provider types are +experimental and raise the `GHCP001` diagnostic; suppress it with +`#pragma warning disable GHCP001` or `GHCP001`. + +```csharp +#pragma warning disable GHCP001 // Skill providers are experimental. + +public sealed class MySkillProvider : ISkillProvider +{ + public Task> ListSkillsAsync(CancellationToken cancellationToken) => + Task.FromResult>( + [ + new() + { + Name = "project-facts", + Description = "Important facts about this host application", + ArgumentHint = "" + } + ]); + + public Task ReadSkillAsync(string name, CancellationToken cancellationToken) => + Task.FromResult(name == "project-facts" + ? """ + # Project facts + + Use the application's project index before answering. + """ + : null); +} + +await using var client = new CopilotClient(new CopilotClientOptions +{ + Mode = CopilotClientMode.Empty +}); + +await using var session = await client.CreateSessionAsync(new SessionConfig +{ + AvailableTools = [], // Empty mode requires an explicit tool allow-list. + SkillProvider = new MySkillProvider(), + EnableSkills = true // Required in empty mode; otherwise skills default off. +}); +``` + +The runtime may call `ListSkillsAsync` and `ReadSkillAsync` concurrently. Honor +the supplied cancellation token; it is canceled when the runtime abandons the +request (for example, on timeout, session disposal, or a resume that replaces the +provider) or the connection closes. Return `null` from `ReadSkillAsync` when a +skill name is not found. Optional `SkillProviderDescriptor` properties are +omitted from JSON when unset. + ##### `PingAsync(string? message = null): Task` Ping the server to check connectivity. @@ -357,6 +439,19 @@ await session.SendAndWaitAsync(new MessageOptions Agent sources serialize as `agent-`. Pass the agent ID without adding a prefix. The SDK preserves its case and whitespace and rejects null IDs. +##### `SetToolsAsync(ICollection tools, CancellationToken cancellationToken = default): Task` (experimental) + +Replace the complete set of externally implemented tools supplied by this client +on a live session. Pass the same tool declarations used in `SessionConfig.Tools` +or `ResumeSessionConfig.Tools`; an empty collection removes this client's tools. +Built-in, MCP/plugin, extension, subagent, and other clients' tools are unchanged. + +Handlers switch after the runtime accepts the replacement. Running tool calls +finish on the handlers that started them, rejected replacements leave the +previous handlers installed, and concurrent replacements are applied in order. +See [Changing tools](../docs/features/changing-tools.md) for shared behavior and +active-turn limitations. + ##### Structured outputs (experimental) Use `SendAndWaitAsync` to infer a JSON Schema from a .NET type and @@ -505,6 +600,13 @@ Abort the currently processing message in this session. Get all events/messages from this session. +Live notifications and history deserialization select source-generated metadata for the +received event type rather than initializing every event type on the first event. +History parsing uses a bounded stack or pooled UTF-8 buffer to avoid an extra +reader-scoping pass; common event-type selection does not allocate a discriminator string. +Unknown or missing event types retain the base `SessionEvent` fallback; serialization +and malformed-event validation are unchanged. + ##### `DisposeAsync(): ValueTask` Close the session and release in-memory resources. Session data on disk is preserved — the conversation can be resumed later via `ResumeSessionAsync()`. To permanently delete session data, use `client.DeleteSessionAsync()`. @@ -521,6 +623,24 @@ await session2.DisposeAsync(); --- +## Provider-qualified model selection + +Use the experimental request-object overload to select the `modelId` and `providerId` from a model catalog entry: + +```csharp +using GitHub.Copilot.Rpc; + +#pragma warning disable GHCP001 // Provider-qualified model selection is experimental. +await session.Rpc.Model.SwitchToAsync(new ModelSwitchToRequest +{ + ModelId = modelId, + ProviderId = providerId, +}); +#pragma warning restore GHCP001 +``` + +The SDK supplies the session ID. Existing positional calls, including `session.Rpc.Model.SwitchToAsync("auto", AutoTier.Balance)`, retain their original parameter order and defaults. + ## Auto routing tiers The canonical values are `AutoTier.Efficiency`, `AutoTier.Balance`, `AutoTier.Intelligence`, and `AutoTier.Fast`, which send `efficiency`, `balance`, `intelligence`, and `fast` on the wire. Fast is an integrator-only latency preset, not a fourth first-party GitHub Copilot preference. The SDK forwards the requested value without deciding eligibility or inspecting client identity. An externally supplied older runtime returns its native runtime or JSON-RPC error; the SDK does not downgrade or silently ignore the request. @@ -797,6 +917,16 @@ var session = await client.CreateSessionAsync(new SessionConfig }); ``` +An explicit `apply_patch` override may advertise a root string schema instead of +an object schema. The runtime delivers the patch as a string in +`ToolInvocation.Arguments`; the SDK binds that scalar to the named `input` +argument required by `AIFunction`. A schema wrapper around a typed handler should +therefore use a parameter named `input`. Object-schema tools retain their +declared parameter names. + +String-schema `apply_patch` overrides cannot contain JSON Schema references; +use an object schema if references are needed. + #### Skipping Permission Prompts Set `CopilotToolOptions.SkipPermission` to allow a tool to execute without triggering a permission prompt: @@ -975,6 +1105,8 @@ var session = await client.CreateSessionAsync(new SessionConfig Available section IDs are defined as static properties on the `SystemMessageSection` struct: `Preamble`, `Identity`, `Tone`, `ToolEfficiency`, `EnvironmentContext`, `CodeChangeRules`, `Guidelines`, `Safety`, `ToolInstructions`, `CustomInstructions`, `RuntimeInstructions`, `LastInstructions`. `Identity` and `ToolInstructions` are section groups that target a collection of related sub-sections as a unit; use `Preamble` to target just the identity preamble. +`SystemMessageSection.LastInstructions` (`last_instructions`) includes configured subagent-model guidance when the `task` tool is available. Removing or replacing this section also removes that guidance; a `SectionOverride.Transform` callback receives the complete section, including the guidance, and its returned content is authoritative. Append, prepend, and preserve retain their usual section semantics. These overrides change prompt prose only, not configured subagent models, tool availability, or runtime dispatch policy. `SystemMessageSection.RuntimeInstructions` is a separate section: removing it does not remove `SystemMessageSection.LastInstructions`. + Each section override supports five actions: `Replace`, `Remove`, `Append`, `Prepend`, and `Preserve` (a no-op that opts an individually-addressable section out of a group-level `Remove`). Unknown section IDs are handled gracefully: content is appended to additional instructions, and `Remove` overrides are silently ignored. #### Replace Mode @@ -1263,6 +1395,32 @@ var session = await client.CreateSessionAsync(new SessionConfig - `OnSessionStart` - Run logic when a session starts or resumes. - `OnSessionEnd` - Cleanup or logging when session ends. - `OnErrorOccurred` - Handle errors with retry/skip/abort strategies. +- `OnAgentStop` - Intercept a natural stop of the top-level agent. +- `OnSubagentStart` - Add context to a sub-agent before its first turn. +- `OnSubagentStop` - Inspect a sub-agent's last response, block the stop with a follow-up instruction, or replace its response. + +Sub-agent lifecycle hooks are registered on the parent session. Their input includes the parent `SessionId`, a `DateTimeOffset` timestamp, `WorkingDirectory`, `TranscriptPath` (empty if unavailable), and `AgentName`; `AgentDisplayName` and `AgentDescription` are optional. `OnSubagentStop` also receives an optional `AgentId`, `AgentType`, `StopReason` (currently `"end_turn"`), and the sub-agent's `Response`. `HookInvocation.SessionId` is the parent session ID. + +```csharp +var session = await client.CreateSessionAsync(new SessionConfig +{ + Hooks = new SessionHooks + { + OnSubagentStart = (input, invocation) => + Task.FromResult(new SubagentStartHookOutput + { + AdditionalContext = "Read the requested file before answering." + }), + OnSubagentStop = (input, invocation) => + Task.FromResult(new SubagentStopHookOutput + { + ModifiedResponse = input.Response.Trim() + }) + } +}); +``` + +`OnSubagentStart`'s `AdditionalContext` is prepended to the child's initial prompt. To request another child turn instead of accepting its response, return `new SubagentStopHookOutput { Decision = "block", Reason = "Explain your findings." }` from `OnSubagentStop`; a non-empty `Reason` is required. Otherwise, `ModifiedResponse` replaces the response reported to the parent. These hooks are distinct from `OnAgentStop`, which applies to the top-level agent. ## Elicitation Requests diff --git a/dotnet/src/Canvas.cs b/dotnet/src/Canvas.cs index 6bf8be984e..02874a79f2 100644 --- a/dotnet/src/Canvas.cs +++ b/dotnet/src/Canvas.cs @@ -171,6 +171,12 @@ public interface ICanvasHandler /// Handle a non-lifecycle action declared by the canvas. /// Default: throws . /// + /// + /// The returned value is sent to the model as the invoke_canvas_action + /// tool result. To return text and images, as tool handlers do, return a + /// with ; + /// any other value is rendered to the model as JSON text. + /// Task OnActionAsync(CanvasProviderInvokeActionRequest context, CancellationToken cancellationToken); } diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index e2da8747be..d22e329428 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -71,6 +71,8 @@ public sealed partial class CopilotClient : IDisposable, IAsyncDisposable /// that has not been explicitly disposed or removed. /// internal readonly ConcurrentDictionary _sessions = new(); + private readonly object _sessionAdmissionLock = new(); + private bool _sessionAdmissionClosed; private readonly ConcurrentDictionary>> _gitHubTokenProviders = new(); private readonly CopilotClientOptions _options; @@ -365,7 +367,14 @@ private static bool IsFullyQualifiedPath(string path) /// public Task StartAsync(CancellationToken cancellationToken = default) { - return _connectionTask ??= StartCoreAsync(cancellationToken); + lock (_sessionAdmissionLock) + { + if (_connectionTask is null) + { + _sessionAdmissionClosed = false; + } + return _connectionTask ??= StartCoreAsync(cancellationToken); + } async Task StartCoreAsync(CancellationToken ct) { @@ -556,11 +565,12 @@ await InvokeRpcAsync( /// public async Task StopAsync() { + var sessions = CloseSessionAdmission(clearSessions: false); DisconnectAhpHosts(); List errors = []; - CancelPendingExternalTools(); + CancelPendingExternalTools(sessions); - foreach (var session in _sessions.Values.ToArray()) + foreach (var session in sessions) { try { @@ -572,7 +582,6 @@ public async Task StopAsync() } } - _sessions.Clear(); ClearGitHubTokenProviders(); await CleanupConnectionAsync(errors, gracefulRuntimeShutdown: true); @@ -588,6 +597,7 @@ public async Task StopAsync() /// Use this when fails or takes too long. This method: /// /// Clears all sessions immediately without destroying them + /// Stops session event delivery without waiting for running handlers /// Force closes the connection /// Kills the CLI process (if spawned by this client) /// @@ -604,9 +614,13 @@ public async Task StopAsync() /// public async Task ForceStopAsync() { + var sessions = CloseSessionAdmission(clearSessions: true); DisconnectAhpHosts(); - CancelPendingExternalTools(); - _sessions.Clear(); + CancelPendingExternalTools(sessions); + foreach (var session in sessions) + { + session.Retire(); + } ClearGitHubTokenProviders(); var errors = new List(); @@ -614,6 +628,20 @@ public async Task ForceStopAsync() ThrowErrors(errors); } + private CopilotSession[] CloseSessionAdmission(bool clearSessions) + { + lock (_sessionAdmissionLock) + { + _sessionAdmissionClosed = true; + var sessions = _sessions.Values.ToArray(); + if (clearSessions) + { + _sessions.Clear(); + } + return sessions; + } + } + private static void ThrowErrors(List? errors) { if (errors is not null) @@ -632,14 +660,17 @@ private static void ThrowErrors(List? errors) private async Task CleanupConnectionAsync(List? errors, bool gracefulRuntimeShutdown) { - var connectionTask = _connectionTask; + Task? connectionTask; + lock (_sessionAdmissionLock) + { + connectionTask = _connectionTask; + _connectionTask = null; + } if (connectionTask is null) { return; } - _connectionTask = null; - Connection ctx; try { @@ -892,6 +923,7 @@ private CopilotSession InitializeSession( session.RegisterElicitationHandler(config.OnElicitationRequest); session.RegisterExitPlanModeHandler(config.OnExitPlanModeRequest); session.RegisterAutoModeSwitchHandler(config.OnAutoModeSwitchRequest); + session.RegisterSkillProvider(config.SkillProvider); if (config.OnUserInputRequest != null) { session.RegisterUserInputHandler(config.OnUserInputRequest); @@ -911,7 +943,7 @@ private CopilotSession InitializeSession( ConfigureSessionFsHandlers(session, config.CreateSessionFsProvider); session.SetCanvasHandler(config.CanvasHandler); session.RegisterBearerTokenProviders(BuildBearerTokenCallbacks(config)); - RegisterSession(session); + RegisterSession(session, rpc); session.StartProcessingEvents(); LoggingHelpers.LogTiming(_logger, LogLevel.Debug, null, callerName + " local setup complete. Elapsed={Elapsed}, SessionId={SessionId}, Tools={ToolsCount}, Commands={CommandsCount}, Hooks={HasHooks}", @@ -1190,6 +1222,10 @@ public async Task CreateSessionAsync(SessionConfig config, Cance { ArgumentNullException.ThrowIfNull(config); ValidateGitHubTokenConfig(config); + if (config.Cloud is not null && config.SkillProvider is not null) + { + throw new ArgumentException("Skill providers are not supported for cloud sessions."); + } var connection = await EnsureConnectedAsync(cancellationToken); var totalTimestamp = Stopwatch.GetTimestamp(); @@ -1208,7 +1244,9 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.Hooks.OnSessionStart != null || config.Hooks.OnSessionEnd != null || config.Hooks.OnErrorOccurred != null || - config.Hooks.OnAgentStop != null); + config.Hooks.OnAgentStop != null || + config.Hooks.OnSubagentStart != null || + config.Hooks.OnSubagentStop != null); var (wireSystemMessage, transformCallbacks) = ExtractTransformCallbacks(config.SystemMessage); @@ -1227,6 +1265,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance var registrationId = RegisterGitHubTokenProvider(config.GitHubTokenProvider); var registrationTransferred = false; CopilotSession? session = null; + string? serverAssignedSessionId = null; try { if (localSessionId != null) @@ -1308,6 +1347,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance DisabledMcpServers: config.DisabledMcpServers, LargeOutput: config.LargeOutput, ToolSearch: config.ToolSearch, + ImageGeneration: config.ImageGeneration, Memory: config.Memory, Canvases: config.Canvases, RequestCanvasRenderer: config.RequestCanvasRenderer, @@ -1321,10 +1361,12 @@ public async Task CreateSessionAsync(SessionConfig config, Cance FeatureFlags: config.FeatureFlags, ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, + EnforceManagedModelDefaults: config.EnforceManagedModelDefaults, GitHubMcpToolConfig: config.GitHubMcpToolConfig, ManagedSettings: config.ManagedSettings, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null, - AdditionalDirectories: config.AdditionalDirectories); + AdditionalDirectories: config.AdditionalDirectories, + HasSkillProvider: config.SkillProvider is not null ? true : null); var rpcTimestamp = Stopwatch.GetTimestamp(); @@ -1343,6 +1385,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance && sessionIdProp.GetString() is string sessionId && !string.IsNullOrEmpty(sessionId)) { + serverAssignedSessionId = sessionId; session = InitializeSession( sessionId, connection.Rpc, @@ -1350,6 +1393,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance transformCallbacks, hasHooks, "CopilotClient.CreateSessionAsync"); + serverAssignedSessionId = null; } }; @@ -1391,6 +1435,28 @@ public async Task CreateSessionAsync(SessionConfig config, Cance catch (Exception ex) { session?.Unregister(); + if (localSessionId is null && serverAssignedSessionId is not null) + { + try + { + using var cleanupTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var deletion = await InvokeRpcAsync( + connection.Rpc, "session.delete", [new DeleteSessionRequest(serverAssignedSessionId)], + cleanupTimeout.Token).ConfigureAwait(false); + if (!deletion.Success) + { + throw new InvalidOperationException($"Failed to delete session {serverAssignedSessionId}: {deletion.Error}"); + } + } + catch (Exception cleanupError) when (cleanupError is OperationCanceledException + or IOException + or SocketException + or InvalidOperationException + or ObjectDisposedException) + { + _logger.LogWarning(cleanupError, "Failed to delete cloud session {SessionId} after creation failed", serverAssignedSessionId); + } + } if (ex is not OperationCanceledException) { @@ -1465,7 +1531,9 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config.Hooks.OnSessionStart != null || config.Hooks.OnSessionEnd != null || config.Hooks.OnErrorOccurred != null || - config.Hooks.OnAgentStop != null); + config.Hooks.OnAgentStop != null || + config.Hooks.OnSubagentStart != null || + config.Hooks.OnSubagentStop != null); var (wireSystemMessage, transformCallbacks) = ExtractTransformCallbacks(config.SystemMessage); @@ -1552,6 +1620,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes DisabledMcpServers: config.DisabledMcpServers, LargeOutput: config.LargeOutput, ToolSearch: config.ToolSearch, + ImageGeneration: config.ImageGeneration, Memory: config.Memory, Canvases: config.Canvases, RequestCanvasRenderer: config.RequestCanvasRenderer, @@ -1566,11 +1635,13 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes FeatureFlags: config.FeatureFlags, ExpAssignments: config.ExpAssignments, EnableManagedSettings: config.EnableManagedSettings, + EnforceManagedModelDefaults: config.EnforceManagedModelDefaults, GitHubMcpToolConfig: config.GitHubMcpToolConfig, ManagedSettings: config.ManagedSettings, EnableGitHubTelemetryForwarding: _options.OnGitHubTelemetry != null ? true : null, AdditionalDirectories: config.AdditionalDirectories, - AllowTranscriptRecovery: config.AllowTranscriptRecovery); + AllowTranscriptRecovery: config.AllowTranscriptRecovery, + HasSkillProvider: config.SkillProvider is not null ? true : null); var rpcTimestamp = Stopwatch.GetTimestamp(); var response = await InvokeRpcAsync( @@ -1784,6 +1855,7 @@ public async Task DeleteSessionAsync(string sessionId, CancellationToken cancell if (_sessions.TryRemove(sessionId, out var session)) { + session.ClearSkillProvider(); session.ReleaseGitHubTokenProviderRegistration(); } } @@ -2196,8 +2268,13 @@ private void ConfigureSessionFsHandlers(CopilotSession session, Func ConnectToServerAsync(Process? cliProcess, string? rpc.SetLocalRpcMethod("autoModeSwitch.request", handler.OnAutoModeSwitchRequest); rpc.SetLocalRpcMethod("hooks.invoke", handler.OnHooksInvoke); rpc.SetLocalRpcMethod("systemMessage.transform", handler.OnSystemMessageTransform); + rpc.SetLocalRpcMethod("skillProvider.list", handler.OnSkillProviderList, singleObjectParam: true); + rpc.SetLocalRpcMethod("skillProvider.read", handler.OnSkillProviderRead, singleObjectParam: true); ClientSessionApiRegistration.RegisterClientSessionApiHandlers(rpc, sessionId => { var session = GetSession(sessionId) ?? throw new ArgumentException($"Unknown session {sessionId}"); @@ -2828,14 +2907,15 @@ private async Task CancelExternalToolsWhenConnectionClosesAsync(JsonRpc rpc) CancelPendingExternalTools(); } - private void CancelPendingExternalTools() + private void CancelPendingExternalTools(IEnumerable? sessions = null) { if (_clientGlobalApis?.LlmInference is LlmInferenceAdapter llmInferenceAdapter) { llmInferenceAdapter.CancelPending(); } - foreach (var session in _sessions.Values) + foreach (var session in sessions ?? _sessions.Values) { + session.ClearSkillProvider(); session.CancelPendingExternalTools(); } } @@ -2864,6 +2944,8 @@ private static JsonSerializerOptions CreateSerializerOptions() DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; + options.Converters.Add(SessionEventJsonConverter.Default); + options.TypeInfoResolverChain.Add(SessionEventJsonTypeInfoResolver.Default); options.TypeInfoResolverChain.Add(ClientJsonContext.Default); options.TypeInfoResolverChain.Add(TypesJsonContext.Default); options.TypeInfoResolverChain.Add(CopilotSession.SessionJsonContext.Default); @@ -2881,12 +2963,29 @@ private static JsonSerializerOptions CreateSerializerOptions() return session; } - private void RegisterSession(CopilotSession session) + private void RegisterSession(CopilotSession session, JsonRpc rpc) { - if (!_sessions.TryAdd(session.SessionId, session)) + Exception error; + lock (_sessionAdmissionLock) { - throw new InvalidOperationException($"Session '{session.SessionId}' is already tracked by this client."); + // A restarted connection must not admit setup that began on the stopped transport. + if (!_sessionAdmissionClosed + && _connectionTask?.Status == System.Threading.Tasks.TaskStatus.RanToCompletion + && ReferenceEquals(_connectionTask.Result.Rpc, rpc)) + { + if (_sessions.TryAdd(session.SessionId, session)) + { + return; + } + error = new InvalidOperationException($"Session '{session.SessionId}' is already tracked by this client."); + } + else + { + error = new IOException("Cannot register a session on a stopped connection."); + } } + session.Retire(); + throw error; } /// @@ -3009,6 +3108,32 @@ public async ValueTask OnSystemMessageTransfo return await session.HandleSystemMessageTransformAsync(sections); } + public async ValueTask OnSkillProviderList(SkillProviderListRequest request, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(request); + if (string.IsNullOrEmpty(request.SessionId)) + { + throw new ArgumentException("Skill provider list request is missing a session id."); + } + + var session = client.GetSession(request.SessionId) + ?? throw new InvalidOperationException($"No skill provider for session: {request.SessionId}"); + return await session.HandleSkillProviderListAsync(cancellationToken); + } + + public async ValueTask OnSkillProviderRead(SkillProviderReadRequest request, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(request); + if (string.IsNullOrEmpty(request.SessionId) || string.IsNullOrEmpty(request.Name)) + { + throw new ArgumentException("Skill provider read request is missing a session id or skill name."); + } + + var session = client.GetSession(request.SessionId) + ?? throw new InvalidOperationException($"No skill provider for session: {request.SessionId}"); + return await session.HandleSkillProviderReadAsync(request.Name, cancellationToken); + } + } private class Connection( @@ -3150,6 +3275,7 @@ internal record CreateSessionRequest( [property: JsonPropertyName("disabledMcpServers")] IList? DisabledMcpServers = null, LargeToolOutputConfig? LargeOutput = null, ToolSearchConfig? ToolSearch = null, + ImageGenerationConfig? ImageGeneration = null, MemoryConfiguration? Memory = null, #pragma warning disable GHCP001 IList? Canvases = null, @@ -3164,10 +3290,12 @@ internal record CreateSessionRequest( [property: JsonPropertyName("featureFlags")] IDictionary? FeatureFlags = null, [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, + [property: JsonPropertyName("enforceManagedModelDefaults")] bool? EnforceManagedModelDefaults = null, [property: JsonPropertyName("managedSettings")] ManagedSettings? ManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null, - IList? AdditionalDirectories = null); + IList? AdditionalDirectories = null, + bool? HasSkillProvider = null); #pragma warning restore GHCP001 internal record ToolDefinition( @@ -3270,6 +3398,7 @@ internal record ResumeSessionRequest( [property: JsonPropertyName("disabledMcpServers")] IList? DisabledMcpServers = null, LargeToolOutputConfig? LargeOutput = null, ToolSearchConfig? ToolSearch = null, + ImageGenerationConfig? ImageGeneration = null, MemoryConfiguration? Memory = null, #pragma warning disable GHCP001 IList? Canvases = null, @@ -3285,11 +3414,13 @@ internal record ResumeSessionRequest( [property: JsonPropertyName("featureFlags")] IDictionary? FeatureFlags = null, [property: JsonPropertyName("expAssignments")] CopilotExpAssignmentResponse? ExpAssignments = null, [property: JsonPropertyName("enableManagedSettings")] bool? EnableManagedSettings = null, + [property: JsonPropertyName("enforceManagedModelDefaults")] bool? EnforceManagedModelDefaults = null, [property: JsonPropertyName("managedSettings")] ManagedSettings? ManagedSettings = null, bool? EnableGitHubTelemetryForwarding = null, [property: JsonPropertyName("githubMcpToolConfig")] GitHubMcpToolConfig? GitHubMcpToolConfig = null, IList? AdditionalDirectories = null, - bool? AllowTranscriptRecovery = null); + bool? AllowTranscriptRecovery = null, + bool? HasSkillProvider = null); #pragma warning restore GHCP001 internal record ResumeSessionResponse( @@ -3377,6 +3508,21 @@ internal record UserInputRequestResponse( string Answer, bool WasFreeform); + internal record SkillProviderListRequest( + string SessionId); + +#pragma warning disable GHCP001 + internal record SkillProviderListResult( + IReadOnlyList Skills); +#pragma warning restore GHCP001 + + internal record SkillProviderReadRequest( + string SessionId, + string Name); + + internal record SkillProviderReadResult( + [property: JsonIgnore(Condition = JsonIgnoreCondition.Never)] string? Markdown); + internal record AutoModeSwitchRequestResponse( AutoModeSwitchResponse Response); @@ -3405,6 +3551,12 @@ internal record HooksInvokeResponse( [JsonSerializable(typeof(ExitPlanModeResult))] [JsonSerializable(typeof(GetLastSessionIdResponse))] [JsonSerializable(typeof(HooksInvokeResponse))] + [JsonSerializable(typeof(SkillProviderListRequest))] +#pragma warning disable GHCP001 + [JsonSerializable(typeof(SkillProviderListResult))] +#pragma warning restore GHCP001 + [JsonSerializable(typeof(SkillProviderReadRequest))] + [JsonSerializable(typeof(SkillProviderReadResult))] [JsonSerializable(typeof(ListSessionsRequest))] [JsonSerializable(typeof(ListSessionsResponse))] [JsonSerializable(typeof(GetSessionMetadataRequest))] diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs index dc5e45849e..6ab5009adc 100644 --- a/dotnet/src/Generated/Rpc.cs +++ b/dotnet/src/Generated/Rpc.cs @@ -254,6 +254,56 @@ internal sealed class HostPublishSessionRequest public string SessionId { get; set; } = string.Empty; } +/// Metadata advertised by the AHP host, not a guarantee that history can be resumed. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HostSessionSummary +{ + /// Optional activity description advertised by the host. + [JsonPropertyName("activity")] + public string? Activity { get; set; } + + /// 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; + + /// Unsigned 32-bit AHP session status bitset (0..=4294967295); unknown bits must be preserved. + [JsonPropertyName("status")] + public long Status { get; set; } + + /// Authoritative title advertised by the host. + [JsonPropertyName("title")] + public string Title { get; set; } = string.Empty; +} + +/// The complete live and dormant advertised catalog, without subscribing to sessions. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HostListSessionsResult +{ + /// Complete advertised session summaries, in the host's catalog order. + [JsonPropertyName("sessions")] + public IList Sessions { get => field ??= []; set; } +} + +/// Reads a running host's catalog or the runtime's saved default catalog. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HostListSessionsRequest +{ + /// + /// 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; } +} + /// The resident session the application has materialized on its own connection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] internal sealed class HostCreateSessionResult @@ -275,6 +325,13 @@ internal sealed class HostCreateSessionRequest [JsonPropertyName("handoffId")] public string HandoffId { get; set; } = string.Empty; + /// + /// 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; } @@ -331,6 +388,10 @@ public sealed class HostGitHubEnvironmentOptions /// 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; } } /// Local WebSocket transport options. @@ -356,15 +417,19 @@ public sealed class HostLocalServerOptions /// Starts a supervised AHP host with at least one explicitly selected transport. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HostStartRequest +public sealed class HostStartRequest { + /// 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; } + /// Registers a GitHub Mission Control environment and enables its relay transport. [JsonPropertyName("githubEnvironment")] public HostGitHubEnvironmentOptions? GitHubEnvironment { get; set; } /// Caller-generated UUID identifying this connection-owned listener. [JsonPropertyName("hostId")] - public string HostId { get; set; } = string.Empty; + public required string HostId { get; set; } /// Enables a local WebSocket listener. [JsonPropertyName("localServer")] @@ -576,6 +641,8 @@ public sealed class ModelBillingTokenPricesLongContext [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; } @@ -596,6 +663,8 @@ public sealed class ModelBillingTokenPricesLongContext [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; } @@ -625,6 +694,8 @@ public sealed class ModelBillingTokenPrices [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; } @@ -645,6 +716,8 @@ public sealed class ModelBillingTokenPrices [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; } @@ -737,6 +810,10 @@ public sealed class ModelCapabilitiesSupports [JsonPropertyName("reasoningEffort")] public bool? ReasoningEffort { get; set; } + /// Whether the model supports provider-native thinking. Independent of configurable reasoning effort; omission means unknown. + [JsonPropertyName("thinking")] + public bool? Thinking { get; set; } + /// Whether this model supports canonical tool calling. [JsonPropertyName("toolCalls")] public bool? ToolCalls { get; set; } @@ -785,23 +862,6 @@ public sealed class ModelPolicy public string? Terms { get; set; } } -/// A neutral reference to the model provider that produced a model: an opaque id, a human-readable label, and the provider kind. Carried on each enumerated Model so consumers can group by provider without reaching into a provider-shaped internal type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderRef -{ - /// Opaque, stable id of the provider that produced this model. Matches the enumerated `ModelProviderDescriptor.id`. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// The provider kind. - [JsonPropertyName("kind")] - public ModelProviderKind Kind { get; set; } - - /// Human-readable provider label, owned by the runtime so every consumer renders identical text. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; -} - /// Service-published warning text that hosts should display when presenting a model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ModelWarningText @@ -867,6 +927,10 @@ public sealed class Model [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; } + /// 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; } @@ -920,7 +984,7 @@ public sealed class BuiltInModelCatalog [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class SandboxHostCapability { - /// 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, and a policy that uses it must also set `network.allowLocalNetwork`), `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). + /// 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; @@ -950,6 +1014,234 @@ public sealed class SandboxHostSupport public bool Supported { get; set; } } +/// Status of the persistent certificate authority of the sandbox credential proxy. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxProxyCaStatus +{ + /// 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; } + + /// 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; } + + /// The state of the certificate authority. + [JsonPropertyName("state")] + public SandboxProxyCaState State { 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxConfigAuth +{ + /// 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; } +} + +/// 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; } +} + +/// Whole-value environment credential masking for sandboxed children. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxCredentialsConfig +{ + /// 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; } +} + +/// 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; } +} + +/// 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; } +} + +/// Filesystem rules to merge into the base policy. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxConfigUserPolicyFilesystem +{ + /// Whether to clear the policy when the session exits. + [JsonPropertyName("clearPolicyOnExit")] + public bool? ClearPolicyOnExit { get; set; } + + /// Paths explicitly denied. + [JsonPropertyName("deniedPaths")] + public IList? DeniedPaths { get; set; } + + /// Paths granted read-only access. + [JsonPropertyName("readonlyPaths")] + public IList? ReadonlyPaths { get; set; } + + /// Paths granted read/write access. + [JsonPropertyName("readwritePaths")] + public IList? ReadwritePaths { get; set; } +} + +/// HTTP proxy configuration for sandboxed traffic. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +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; } + + /// 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; } +} + +/// Network rules to merge into the base policy. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxConfigUserPolicyNetwork +{ + /// 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; } + + /// Whether traffic to local/loopback addresses is allowed. + [JsonPropertyName("allowLocalNetwork")] + public bool? AllowLocalNetwork { get; set; } + + /// Whether outbound network traffic is allowed at all. + [JsonPropertyName("allowOutbound")] + public bool? AllowOutbound { 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; } + + /// 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; } +} + +/// macOS seatbelt-specific options. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxConfigUserPolicySeatbelt +{ + /// Whether the macOS seatbelt profile may access the keychain. + [JsonPropertyName("keychainAccess")] + public bool? KeychainAccess { get; set; } +} + +/// User-managed sandbox policy fragment merged into the auto-discovered base policy. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxConfigUserPolicy +{ + /// 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; } + + /// macOS seatbelt options to merge into the base policy. + [JsonPropertyName("seatbelt")] + public SandboxConfigUserPolicySeatbelt? Seatbelt { get; set; } +} + +/// Resolved sandbox configuration. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxConfig +{ + /// 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; } + + /// 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; } + + /// Whether sandboxing is enabled for the session. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// 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; } +} + +/// 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 SandboxProxyCaRequest +{ + /// 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; } +} + +/// Result of creating the persistent certificate authority of the sandbox credential proxy. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxProxyCaCreateResult +{ + /// Absolute path of the public certificate of the certificate authority, in PEM format. + [JsonPropertyName("certificatePath")] + public string CertificatePath { get; set; } = string.Empty; +} + /// 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 @@ -1069,6 +1361,7 @@ internal sealed class AccountGetQuotaRequest [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 @@ -1565,6 +1858,24 @@ public partial class AuthInfoUser : AuthInfo public required string Login { 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"; + + /// 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; } +} + /// 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)] @@ -6514,6 +6825,128 @@ internal sealed class AgentsGetDiscoveryPathsRequest 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; } +} + +/// 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; +} + +/// The shipped agent whose definition to load. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AgentsGetBuiltinDefinitionRequest +{ + /// 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; +} + +/// 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; +} + +/// 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; +} + +/// 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)] +internal sealed class AgentsCustomAgentInitialModelDecisionResult +{ + /// 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; } +} + +/// The models a custom agent asks for, and the models actually available. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AgentsCustomAgentInitialModelDecisionParams +{ + /// 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; +} + /// Loaded instruction source for a session, including path, content, category, location, applicability, and optional description. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class InstructionSource @@ -6628,6 +7061,179 @@ internal sealed class InstructionsGetDiscoveryPathsRequest public IList? ProjectPaths { get; set; } } +/// 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 InstalledPlugin +{ + /// 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; + + /// 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; } + + /// 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; } +} + +/// An account the host has signed in to, identified by the server it lives on and the login it uses there. The same person can appear more than once when they use both github.com and an Enterprise server. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class LoggedInUser +{ + /// Source account this account was derived from, when one was recorded. + [JsonPropertyName("derivedFrom")] + public string? DerivedFrom { get; set; } + + /// Host the account belongs to, such as `github.com` or an Enterprise server. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; + + /// Account kind, when the host recorded one. Consumers must tolerate new strings. + [JsonPropertyName("kind")] + public string? Kind { get; set; } + + /// Account login on that host. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; +} + +/// 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 GlobalStateLoadResult +{ + /// Whether the user has answered the prompt suggesting they install the desktop app. + [JsonPropertyName("appInstallNudgeResponded")] + public bool? AppInstallNudgeResponded { get; set; } + + /// Whether the app tip has been shown. + [JsonPropertyName("appTipShown")] + public bool? AppTipShown { 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; } + + /// When the Auto-feedback hint was last shown, as an ISO 8601 timestamp. It enforces the once-per-day cap for non-staff users across restarts. + [JsonPropertyName("autoFeedbackLastPromptedAt")] + public string? AutoFeedbackLastPromptedAt { get; set; } + + /// When the host first ran on this machine. + [JsonPropertyName("firstLaunchAt")] + public string? FirstLaunchAt { get; set; } + + /// Plugins installed on this machine. + [JsonPropertyName("installedPlugins")] + public IList? InstalledPlugins { get; set; } + + /// Account used for the most recent sign-in. + [JsonPropertyName("lastLoggedInUser")] + public LoggedInUser? LastLoggedInUser { get; set; } + + /// Every account the host has signed in to on this machine. + [JsonPropertyName("loggedInUsers")] + public IList? LoggedInUsers { get; set; } + + /// Whether the one-off cleanup of stored reasoning summaries has run. + [JsonPropertyName("reasoningSummariesCleanupDone")] + public bool? ReasoningSummariesCleanupDone { get; set; } + + /// Models the user selected recently, most recent first. + [JsonPropertyName("recentModelIds")] + public IList? RecentModelIds { get; set; } + + /// Whether the user declined to trust the sandbox credential proxy CA. + [JsonPropertyName("sandboxCredentialProxyCaDeclined")] + public bool? SandboxCredentialProxyCaDeclined { get; set; } + + /// Whether the sandbox onboarding has been shown. + [JsonPropertyName("sandboxOnboardingShown")] + public bool? SandboxOnboardingShown { get; set; } + + /// Whether the user is a GitHub or Microsoft staff member, which unlocks internal-only behavior. + [JsonPropertyName("staff")] + public bool? Staff { get; set; } + + /// Whether the user was recognized as GitHub staff. + [JsonPropertyName("staffGithub")] + public bool? StaffGitHub { get; set; } + + /// When the staff-only log level migration last ran. + [JsonPropertyName("staffLogLevelMigrationAt")] + public string? StaffLogLevelMigrationAt { get; set; } + + /// Whether the user was recognized as Microsoft staff. + [JsonPropertyName("staffMicrosoft")] + public bool? StaffMicrosoft { get; set; } + + /// When the staff-only model reset last ran. + [JsonPropertyName("staffModelResetAt")] + public string? StaffModelResetAt { get; set; } + + /// When the staff-only update channel migration last ran. + [JsonPropertyName("staffUpdateChannelMigrationAt")] + public string? StaffUpdateChannelMigrationAt { get; set; } + + /// Folders where the user declined the init prompt, so it stays hidden there. + [JsonPropertyName("suppressInitFolders")] + public IList? SuppressInitFolders { get; set; } + + /// Folders the user has marked as trusted. + [JsonPropertyName("trustedFolders")] + public IList? TrustedFolders { get; set; } +} + +/// 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; } +} + +/// 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; } + + /// 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; + + /// Value to store for the key. Omit it, or pass null, to remove the key instead. + [JsonPropertyName("value")] + public JsonElement? Value { get; set; } +} + /// A literal choice the command input accepts, with a human-facing description. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class SlashCommandInputChoice @@ -6729,7 +7335,7 @@ public sealed class UserSettingMetadata public JsonElement Value { get; set; } } -/// Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. +/// Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class UserSettingsGetResult { @@ -6738,15 +7344,6 @@ public sealed class UserSettingsGetResult public IDictionary Settings { get => field ??= new Dictionary(); set; } } -/// Outcome of writing user settings. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UserSettingsSetResult -{ - /// Top-level keys whose write landed in settings.json but is shadowed by a value still present in the legacy config.json (config.json wins on read). The write does not take effect until the legacy value is removed. - [JsonPropertyName("shadowedKeys")] - public IList ShadowedKeys { get => field ??= []; set; } -} - /// Partial user settings to write to settings.json. Each top-level key is written individually, replacing the existing value; a key whose value is null is removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] internal sealed class UserSettingsSetRequest @@ -6756,6 +7353,217 @@ internal sealed class UserSettingsSetRequest public JsonElement Settings { get; set; } } +/// Owner, name, and host of a GitHub repository, as resolved from a git remote URL. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class GitHubRepositoryIdentity +{ + /// Host the remote points at, for example `github.com` or a GitHub Enterprise hostname. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; + + /// 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; +} + +/// 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 GitHubRepositoryAtPathResult +{ + /// Resolved repository identity, or null when the selected remote resolves to no GitHub host. + [JsonPropertyName("repository")] + public GitHubRepositoryIdentity? Repository { get; set; } +} + +/// 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; +} + +/// A freshly registered request id. Registering it before the listing starts is what lets a cancel that races the request still find the owner listing slot. The id serves one listing only. Long-abandoned unused ids can be released by later allocations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitHubOwnersRequestIdResult +{ + /// Request id to pass to `gitHubOwners.list` and, to abandon it, `gitHubOwners.cancel`. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } +} + +/// 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; + + /// 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; +} + +/// 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 GitHubOwnersListResult +{ + /// 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; } +} + +/// Credential to list owners under, and the request id that makes the listing cancellable. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitHubOwnersListRequest +{ + /// The credential the listing runs under, carried opaquely because its shape is the host's own and the runtime only resolves a token and a GitHub host from it. No credential travels: this selects one the runtime already holds. + [JsonPropertyName("authInfo")] + public JsonElement AuthInfo { get; set; } + + /// Request id from `gitHubOwners.nextRequestId`. An id that was never registered, canceled before use, released after being abandoned, or already used is refused rather than silently running uncancellable. + [JsonPropertyName("requestId")] + public long RequestId { get; set; } +} + +/// 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; } +} + +/// 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitCurrentBranchRemoteResult +{ + /// 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; +} + +/// Working-tree path a git query applies to. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitCwdRequest +{ + /// 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; +} + +/// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionWorkingDirectoryContext +{ + /// Merge-base commit SHA (fork point from the remote default branch). + [JsonPropertyName("baseCommit")] + public string? BaseCommit { get; set; } + + /// Current git branch name. + [JsonPropertyName("branch")] + public string? Branch { get; set; } + + /// Current working directory path. + [JsonPropertyName("cwd")] + public string Cwd { get; set; } = string.Empty; + + /// Root directory of the git repository, resolved via git rev-parse. + [JsonPropertyName("gitRoot")] + public string? GitRoot { get; set; } + + /// Head commit of the current git branch. + [JsonPropertyName("headCommit")] + public string? HeadCommit { get; set; } + + /// Hosting platform type of the repository. + [JsonPropertyName("hostType")] + public SessionWorkingDirectoryContextHostType? HostType { get; set; } + + /// 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; } +} + +/// A GitHub repository one of a working tree's remotes points at. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitRemoteRepository +{ + /// GitHub host serving the repository, which is not `github.com` for a GitHub Enterprise remote. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; + + /// Repository name, without the owner. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Account or organization owning the repository. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; + + /// Name of the first remote that produced this distinct repository entry, such as `origin` or `upstream`. + [JsonPropertyName("remoteName")] + public string RemoteName { get; set; } = string.Empty; +} + +/// The GitHub repositories a working tree's remotes point at. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitReposFromRemotesResult +{ + /// One entry per distinct GitHub repository, in the order git reports the first remote for each repository. Empty when no remote points at a GitHub host, which a caller should read as `not connected to GitHub`. Failing to read the remotes is an error, not an empty list. + [JsonPropertyName("repositories")] + public IList Repositories { get => field ??= []; set; } +} + +/// Git working tree whose GitHub remotes should be listed. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class GitReposFromRemotesRequest +{ + /// 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; +} + /// Validated device-managed settings discovered before a session exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ManagedSettingsReadResult @@ -6807,6 +7615,10 @@ public sealed class ManagedSettingMeta [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; } + /// 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; @@ -6820,6 +7632,14 @@ public sealed class ManagedSettingsMeta [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; } + /// Lock state and provenance of `values.model`. [JsonPropertyName("model")] public ManagedSettingMeta? Model { get; set; } @@ -6882,7 +7702,15 @@ public sealed class ManagedSettingsValues [JsonPropertyName("autoTier")] public AutoTier? AutoTier { get; set; } - /// Managed default model identifier, as configured. New sessions start with it; it can name a model the account cannot use, so hosts match it against the listed models. + /// Managed context-tier default for the managed concrete model. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { 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; } } @@ -6931,6 +7759,10 @@ internal sealed class ManagedSettingsResolveRequest /// 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; } + + /// 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; } } /// The authoring JSON schema for managed settings recognized by this runtime. @@ -7036,6 +7868,10 @@ public sealed class SessionFsSetProviderResult [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class SessionFsSetProviderCapabilities { + /// Whether the provider supports binary reads and writes through sessionFs.readFileBytes and sessionFs.writeFileBytes. + [JsonPropertyName("binary")] + public bool? Binary { get; set; } + /// Whether the provider supports SQLite query/exists operations. [JsonPropertyName("sqlite")] public bool? Sqlite { get; set; } @@ -8064,6 +8900,126 @@ internal sealed class SessionsEnrichMetadataRequest public IList Sessions { get => field ??= []; set; } } +/// The workspace record that was written. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsCreateWorkspaceResult +{ + /// The created workspace record, as JSON. + [JsonPropertyName("workspaceJson")] + public string WorkspaceJson { get; set; } = string.Empty; +} + +/// A 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; } + + /// Current git branch name. + [JsonPropertyName("branch")] + public string? Branch { get; set; } + + /// Name of the client that created the session. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } + + /// 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; } +} + +/// Identity, state location and starting context for a workspace record. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsCreateWorkspaceRequest +{ + /// Starting working-directory context. The record keeps `cwd`, `gitRoot`, `repository`, `hostType`, `branch`, and `clientName`. Other fields, including `repositoryHost`, `headCommit`, and `baseCommit`, are ignored. `hostType` must be `github` or `ado`. + [JsonPropertyName("context")] + public SessionWorkingDirectoryContextWithClient? Context { get; set; } + + /// `windows` (any letter case) selects Windows path rules. Any other value selects POSIX path rules. + [JsonPropertyName("convention")] + public string Convention { get; set; } = string.Empty; + + /// 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; + + /// 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; +} + +/// 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 session's state lives, as a root directory and the session ID under it. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsLoadWorkspaceRequest +{ + /// Session ID naming the state directory under the sessions home. Rejected when it is absolute or contains a parent component, so it cannot escape the sessions home. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Root directory every session's state directory sits under. + [JsonPropertyName("sessionsHome")] + public string SessionsHome { get; set; } = string.Empty; +} + +/// The merge completed. The record carries the supplied workspace-schema fields, but a stored `fork_count` stays. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsUpdateWorkspaceFieldsResult +{ +} + +/// Where the session's state lives, plus workspace-schema fields to merge into its workspace record. Stored keys outside the schema are not preserved, and a stored `fork_count` is never replaced. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionsUpdateWorkspaceFieldsRequest +{ + /// Workspace-schema fields to merge into the record, as a JSON object. Fields the object omits keep their stored values, except stored keys outside the schema are not preserved and a stored `fork_count` is never replaced. + [JsonPropertyName("fieldsJson")] + public string FieldsJson { get; set; } = string.Empty; + + /// 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; +} + /// 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 SessionsReloadPluginHooksResult @@ -8111,47 +9067,6 @@ public sealed class SessionsSetAdditionalPluginsResult { } -/// 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 InstalledPlugin -{ - /// 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; - - /// 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; } - - /// 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; } -} - /// Manager-wide additional plugins to register; replaces any previously-configured set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] internal sealed class SessionsSetAdditionalPluginsRequest @@ -8639,81 +9554,135 @@ internal sealed class AgentRegistrySpawnRequest public AgentRegistrySpawnPermissionMode? PermissionMode { get; set; } } -/// Result of a OneAuth token acquisition. -/// Polymorphic base type discriminated by status. +/// Feature availability. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "status", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(EntraTokenAcquireResultOk), "ok")] -[JsonDerivedType(typeof(EntraTokenAcquireResultInteractionRequired), "interaction-required")] -public partial class EntraTokenAcquireResult +public sealed class ConnectorDiscoveryCapabilities { - /// The type discriminator. - [JsonPropertyName("status")] - public virtual string Status { get; set; } = string.Empty; -} + /// 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; } +} -/// The ok variant of . +/// Account metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class EntraTokenAcquireResultOk : EntraTokenAcquireResult +public sealed class ConnectorDiscoveryAuthInfo { - /// - [JsonIgnore] - public override string Status => "ok"; + /// Host. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Opaque access token. - [JsonPropertyName("accessToken")] - public required string AccessToken { get; set; } + /// Login. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Opaque OneAuth account id, when supplied by the broker. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// Authentication type. + [JsonPropertyName("type")] + public AuthInfoType Type { get; set; } +} + +/// Eligible account. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorDiscoveryAccount +{ + /// Opaque account ID. [JsonPropertyName("accountId")] - public string? AccountId { get; set; } + public string AccountId { get; set; } = string.Empty; - /// Expiry as milliseconds since Unix epoch, when supplied by OneAuth. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("expiresOnTimestamp")] - public double? ExpiresOnTimestamp { get; set; } + /// Account metadata. + [JsonPropertyName("authInfo")] + public ConnectorDiscoveryAuthInfo AuthInfo { get => field ??= new(); set; } } -/// The interaction-required variant of . +/// Eligible accounts. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class EntraTokenAcquireResultInteractionRequired : EntraTokenAcquireResult +public sealed class ConnectorDiscoveryAccountList { - /// - [JsonIgnore] - public override string Status => "interaction-required"; + /// Eligible accounts. + [JsonPropertyName("accounts")] + public IList Accounts { get => field ??= []; set; } + + /// Availability. + [JsonPropertyName("availability")] + public ConnectorDiscoveryAvailability Availability { get; set; } } -/// OneAuth token request supplied by a trusted host application. +/// Entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EntraTokenAcquireRequest +public sealed class ConnectorDiscoveryCatalogEntry { - /// Previously rejected token that OneAuth must bypass during renewal. - [JsonPropertyName("accessTokenToRenew")] - public string? AccessTokenToRenew { get; set; } + /// Description. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Public client application id. - [JsonPropertyName("clientId")] - public string ClientId { get; set; } = string.Empty; + /// Display name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Whether the broker may show interaction. - [JsonPropertyName("interaction")] - public EntraTokenInteraction Interaction { get; set; } + /// Logo. + [JsonPropertyName("logo")] + public string? Logo { get; set; } - /// Broker redirect URI registered for the client. Required: the OneAuth broker validates a non-empty, registered redirect URI for the public client (MSAL broker registration), so this is not a browser-flow vestige and cannot be omitted. - [JsonPropertyName("redirectUri")] - public string RedirectUri { get; set; } = string.Empty; + /// Name. + [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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ConnectorDiscoveryCatalogResult +{ + /// Opaque account ID. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; - /// Exact delegated scopes to request. - [JsonPropertyName("scopes")] - public IList Scopes { get => field ??= []; set; } + /// Entries. + [JsonPropertyName("connectors")] + public IList Connectors { get => field ??= []; set; } + + /// Refresh time in Unix epoch milliseconds. + [JsonPropertyName("refreshedAtMs")] + public long RefreshedAtMs { get; set; } + + /// Revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } +} - /// Tenant id or tenant selector, such as common or organizations. - [JsonPropertyName("tenantId")] - public string TenantId { get; set; } = string.Empty; +/// Selected account. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ConnectorDiscoveryAccountRequest +{ + /// Opaque account ID. + [RegularExpression("^\\S+$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [MaxLength(2048)] + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; } /// Identifies the target session. @@ -9079,6398 +10048,6801 @@ internal sealed class LogRequest public string? Url { get; set; } } -/// Managed sandbox enforcement state for a session. +/// Adapter-declared policy that tells clients whether discovery may run automatically. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxEnforcementStatus +public sealed class ModelProviderAutomaticDiscoveryPolicy { - /// Whether an enforcement failure has permanently blocked the session. - [JsonPropertyName("blocked")] - public bool Blocked { get; set; } + /// Whether automatic discovery is allowed, limited to configured providers, or explicit-only. + [JsonPropertyName("mode")] + public ModelProviderAutomaticDiscoveryMode Mode { get; set; } - /// The first sandbox enforcement failure that blocked the session. - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// Maximum network scope used by this adapter during discovery. + [JsonPropertyName("networkScope")] + public ModelProviderDiscoveryNetworkScope NetworkScope { get; set; } - /// Whether the effective managed policy requires an available sandbox backend. - [JsonPropertyName("required")] - public bool Required { 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; } } -/// Identifies the target session. +/// An operation supported by a model-provider adapter. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSandboxGetEnforcementStatusRequest +public sealed class ModelProviderAdapterOperationDescriptor { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } -/// Result of attempting to disable sandboxing for the current session. +/// Contributor attribution, independent of routing identity and authorization. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxDisableForSessionResult +public sealed class ModelProviderAttribution { - /// The authoritative sandbox enabled state after the operation. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Human-readable contributor name, not the adapter display name. + [JsonPropertyName("ownerDisplayName")] + public string? OwnerDisplayName { get; set; } - /// Whether this call resolved the pending request and applied the session opt-out. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Stable contributor identifier. Required and nonblank for extension and custom sources; optional for built-in and configured sources. Does not grant authority. + [JsonPropertyName("ownerId")] + public string? OwnerId { get; set; } + + /// Kind of component that supplied the adapter. Attribution does not confer authority. + [JsonPropertyName("source")] + public ModelProviderProvenanceSource Source { get; set; } } -/// Optional informational context describing how and where the permission decision was made. This does not affect permission behavior. +/// A normalized model-provider adapter in the session's effective catalog. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionDecisionContext +public sealed class ModelProviderAdapterDescriptor { - /// Disposition of the permission request as observed by the responding client. - [JsonPropertyName("outcome")] - public PermissionDecisionOutcome Outcome { get; set; } + /// 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 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; } + /// Adapter-declared policy for passive and automatic discovery. + [JsonPropertyName("automaticDiscovery")] + public ModelProviderAutomaticDiscoveryPolicy AutomaticDiscovery { get => field ??= new(); set; } - /// Controlled reason or actor responsible for the response. - [JsonPropertyName("source")] - public PermissionDecisionSource Source { get; set; } + /// Human-readable provider name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Client surface that submitted the response. - [JsonPropertyName("surface")] - public PermissionDecisionSurface Surface { get; set; } + /// 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; } -/// Request to disable sandboxing for the current session while resolving an active sandbox-bypass permission prompt. +/// Normalized model-provider adapter definitions available to the session, not discovered instances. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SandboxDisableForSessionRequest +public sealed class ModelProviderAdapterCatalog { - /// Optional attribution for the permission decision. - [JsonPropertyName("decisionContext")] - public PermissionDecisionContext? DecisionContext { 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; + /// Available provider adapters ordered by adapterId. + [JsonPropertyName("providers")] + public IList Providers { get => field ??= []; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionProvidersGetCatalogRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of accepting a sandbox path grant. +/// Attribution for the adapter that produced a provider row. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxGrantPathForRequestResult +public sealed class ModelProviderProvenance { - /// Whether this call resolved the pending request and added the path to the session's sandbox policy. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Stable opaque adapter identity from the effective catalog. Treat this as a whole identifier, not a parseable owner or kind. + [JsonPropertyName("adapterId")] + public string AdapterId { get; set; } = string.Empty; + + /// Human-readable contributor name, not the adapter display name. + [JsonPropertyName("ownerDisplayName")] + public string? OwnerDisplayName { get; set; } + + /// Stable contributor identifier when the adapter has an owner outside the runtime. Independent of the contribution mechanism and not a routing key. + [JsonPropertyName("ownerId")] + public string? OwnerId { get; set; } + + /// Descriptive provider family that produced this row; not a routing key. + [JsonPropertyName("providerKind")] + public string ProviderKind { get; set; } = string.Empty; + + /// Kind of component that supplied the adapter. + [JsonPropertyName("source")] + public ModelProviderProvenanceSource Source { get; set; } } -/// Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt. +/// Serializable reference to a discovered provider instance. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SandboxGrantPathForRequestRequest +public sealed class ModelProviderInstanceReference { - /// Optional attribution for the permission decision. - [JsonPropertyName("decisionContext")] - public PermissionDecisionContext? DecisionContext { 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; - /// Identifier of the exact pending sandbox escalation permission request whose sandboxPathGrant to accept. - [JsonPropertyName("requestId")] - public string RequestId { 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; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } -/// Authentication status and account metadata for the session. +/// A normalized model-provider instance discovered by the runtime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionAuthStatus +public sealed class ModelProviderInstance { - /// Authentication type. - [JsonPropertyName("authType")] - public AuthInfoType? AuthType { get; set; } - - /// Copilot plan tier (e.g., individual_pro, business). - [JsonPropertyName("copilotPlan")] - public string? CopilotPlan { get; set; } + /// Human-readable instance name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Authentication host URL. + /// Inference API endpoint when the provider exposes one separately from its management endpoint. [Url] [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("host")] - public string? Host { get; set; } + [JsonPropertyName("inferenceEndpoint")] + public string? InferenceEndpoint { get; set; } - /// Whether the session has resolved authentication. - [JsonPropertyName("isAuthenticated")] - public bool IsAuthenticated { get; set; } + /// Transport to use for inference against this instance. + [JsonPropertyName("inferenceTransport")] + public ProviderEndpointTransport? InferenceTransport { get; set; } - /// Authenticated login/username, if available. - [JsonPropertyName("login")] - public string? Login { get; set; } + /// Provider family to use for inference against this instance. + [JsonPropertyName("inferenceType")] + public ProviderEndpointType? InferenceType { get; set; } - /// Human-readable authentication status description. - [JsonPropertyName("statusMessage")] - public string? StatusMessage { get; set; } -} + /// Wire API to use for inference against this instance, when required by the provider family. + [JsonPropertyName("inferenceWireApi")] + public ProviderEndpointWireApi? InferenceWireApi { 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; + /// 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; } } -/// Indicates whether the credential update succeeded. +/// 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 SessionSetCredentialsResult +public sealed class ModelProviderOperationOutcome { - /// 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; } + /// Machine-readable operation outcome. + [JsonPropertyName("code")] + public ModelProviderOperationOutcomeCode Code { get; set; } - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Human-readable detail for non-success outcomes. + [JsonPropertyName("message")] + public string? Message { get; set; } } -/// Authentication credentials accepted by session.gitHubAuth.setCredentials. Session-owned token-provider identities cannot be installed through this method. -/// Polymorphic base type discriminated by type. +/// Provider instances found by a discovery operation. [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(SettableAuthInfoGhCli), "gh-cli")] -[JsonDerivedType(typeof(SettableAuthInfoApiKey), "api-key")] -public partial class SettableAuthInfo +public sealed class ModelProviderDiscoverResult { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} + /// Discovered provider instances. Empty when passive default discovery finds no reachable provider. + [JsonPropertyName("instances")] + public IList Instances { get => field ??= []; set; } + /// Typed operation outcome. Passive discovery can return `absent` with an empty instance list. + [JsonPropertyName("outcome")] + public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } +} -/// Authentication-info input variant for GitHub-internal HMAC auth, carrying the public GitHub host and HMAC secret. -/// The hmac variant of . +/// Provider discovery parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoHmac : SettableAuthInfo +internal sealed class ModelProviderDiscoverRequest { - /// - [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; } + /// Opaque adapter identity returned by `session.providers.getCatalog`. + [JsonPropertyName("adapterId")] + public string AdapterId { get; set; } = string.Empty; - /// HMAC secret used to sign requests. - [JsonPropertyName("hmac")] - public required string Hmac { 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; } - /// Authentication host. HMAC auth always targets the public GitHub host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// 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 . +/// Current health information for a provider instance. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoEnv : SettableAuthInfo +public sealed class ModelProviderStatus { - /// - [JsonIgnore] - public override string Type => "env"; + /// Normalized provider instance. + [JsonPropertyName("instance")] + public ModelProviderInstance Instance { 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; } + /// Typed operation outcome. + [JsonPropertyName("outcome")] + public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } - /// Name of the environment variable the token was sourced from. - [JsonPropertyName("envVar")] - public required string EnvVar { get; set; } + /// Open provider status value, such as `healthy`, `unreachable`, or `notInstalled`. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; - /// Authentication host (e.g. https://github.com or a GHES host). - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Provider-reported version. + [JsonPropertyName("version")] + public string? Version { 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; } +/// 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; } - /// The token value itself. Treat as a secret. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Token authentication accepted by session.gitHubAuth.setCredentials. -/// The token variant of . +/// Provider-reported model artifact metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoToken : SettableAuthInfo +public sealed class ModelArtifactDetails { - /// - [JsonIgnore] - public override string Type => "token"; + /// Provider-reported model architecture. + [JsonPropertyName("architecture")] + public string? Architecture { 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; } + /// Provider-reported model families. + [JsonPropertyName("families")] + public IList? Families { get; set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Primary model family. + [JsonPropertyName("family")] + public string? Family { get; set; } - /// The token value itself. Treat as a secret. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// Artifact format, such as `gguf`. + [JsonPropertyName("format")] + public string? Format { get; set; } + + /// Provider-reported parameter count label. + [JsonPropertyName("parameterSize")] + public string? ParameterSize { get; set; } + + /// Provider-reported quantization label. + [JsonPropertyName("quantization")] + public string? Quantization { get; set; } + + /// Provider-reported tokenizer. + [JsonPropertyName("tokenizer")] + public string? Tokenizer { get; set; } } -/// Authentication-info variant for direct Copilot API token auth sourced from environment variables, with public GitHub host. -/// The copilot-api-token variant of . +/// A non-fatal provider observation warning. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoCopilotApiToken : SettableAuthInfo +public sealed class ModelProviderWarning { - /// - [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; } + /// Machine-readable warning code. + [JsonPropertyName("code")] + public string Code { get; set; } = string.Empty; - /// Authentication host (always the public GitHub host). - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Human-readable warning message. + [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 . +/// A model offered for agent conversations. Missing capability metadata does not disqualify a candidate. Models known to be incompatible, such as embedding-only models, are excluded by the adapter. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SettableAuthInfoUser : SettableAuthInfo +public sealed class DiscoveredModel { - /// - [JsonIgnore] - public override string Type => "user"; + /// Provider-reported model capabilities. Omitted capability fields are unknown; explicit false values are preserved. + [JsonPropertyName("capabilities")] + public ModelCapabilities Capabilities { 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; } + /// Provider-reported model artifact details. + [JsonPropertyName("details")] + public ModelArtifactDetails Details { get => field ??= new(); set; } - /// Authentication host. - [JsonPropertyName("host")] - public required string Host { get; set; } + /// Provider-reported artifact digest. + [JsonPropertyName("digest")] + public string? Digest { get; set; } - /// OAuth user login. - [JsonPropertyName("login")] - public required string Login { 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; } + + /// Attribution for the adapter that produced this model row. + [JsonPropertyName("provenance")] + public ModelProviderProvenance Provenance { get => field ??= new(); set; } + + /// Provider-reported artifact size in bytes. + [JsonPropertyName("sizeBytes")] + public long? SizeBytes { get; set; } + + /// Non-fatal warnings encountered while enriching this model. + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// Authentication-info input variant for GitHub CLI credentials, carrying host, login, and the `gh auth token` value. -/// The gh-cli variant of . +/// 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)] -public partial class SettableAuthInfoGhCli : SettableAuthInfo +public sealed class DiscoveredModelList { - /// - [JsonIgnore] - public override string Type => "gh-cli"; + /// Provider-native models in provider order. + [JsonPropertyName("models")] + public IList Models { 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; } - - /// 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; } -} - -/// 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"; - - /// 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; } + /// Typed operation outcome. + [JsonPropertyName("outcome")] + public ModelProviderOperationOutcome Outcome { get => field ??= new(); set; } } -/// New auth credentials to install on the session. Omit to leave credentials unchanged. +/// Provider model inventory request parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSetCredentialsParams +internal sealed class ModelProviderModelsListRequest { - /// 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; } + /// 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; } -/// Credential-free authentication identity safe to expose to hosts and user interfaces. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthIdentity -{ - /// Snapshot of the authenticated user's Copilot subscription info, if known. - [JsonPropertyName("copilotUser")] - public CopilotUserResponse? CopilotUser { get; set; } - - /// 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. +/// Vision-specific limits. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthGetCurrentAuthInfoRequest +public sealed class ModelCapabilitiesOverrideLimitsVision { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Maximum image size in bytes. + [JsonPropertyName("max_prompt_image_size")] + public long? MaxPromptImageSize { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthGetAllAuthAvailableRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Maximum number of images per prompt. + [JsonPropertyName("max_prompt_images")] + public long? MaxPromptImages { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthRefreshCopilotUserRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// MIME types the model accepts. + [JsonPropertyName("supported_media_types")] + public IList? SupportedMediaTypes { get; set; } } -/// Internal GitHub login parameters. +/// Token limits for prompts, outputs, and context window. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAuthLoginRequest +public sealed class ModelCapabilitiesOverrideLimits { - /// GitHub host URL. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; - - /// GitHub login. - [JsonPropertyName("login")] - public string Login { get; set; } = string.Empty; + /// Maximum total context window size in tokens. + [JsonPropertyName("max_context_window_tokens")] + public long? MaxContextWindowTokens { get; set; } - /// Whether to persist the token after login. - [JsonPropertyName("persist")] - public bool? Persist { get; set; } + /// Maximum number of output/completion tokens. + [JsonPropertyName("max_output_tokens")] + public long? MaxOutputTokens { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Maximum number of prompt/input tokens. + [JsonPropertyName("max_prompt_tokens")] + public long? MaxPromptTokens { get; set; } - /// GitHub authentication token. - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; + /// Vision-specific limits. + [JsonPropertyName("vision")] + public ModelCapabilitiesOverrideLimitsVision? Vision { get; set; } } -/// Parameters for switching the session's active authentication. +/// Feature flags indicating what the model supports. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAuthSwitchRequest +public sealed class ModelCapabilitiesOverrideSupports { - /// 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; } -} + /// 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; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthLogoutRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Whether this model supports reasoning effort configuration. + [JsonPropertyName("reasoningEffort")] + public bool? ReasoningEffort { get; set; } -/// Parameters identifying a GitHub authentication to log out. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAuthLogoutUserRequest -{ - /// Authentication information to log out. - [JsonPropertyName("authInfo")] - public AuthInfo AuthInfo { get => field ??= new(); set; } + /// Whether this model supports canonical tool calling. + [JsonPropertyName("toolCalls")] + public bool? ToolCalls { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether this model supports vision/image input. + [JsonPropertyName("vision")] + public bool? Vision { get; set; } } -/// Validation error from an authentication attempt. +/// Optional capability overrides (vision, tool_calls, reasoning, etc.). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthValidationError +public sealed class ModelCapabilitiesOverride { - /// Optional message returned by GitHub. - [JsonPropertyName("githubMessage")] - public string? GitHubMessage { get; set; } + /// Token limits for prompts, outputs, and context window. + [JsonPropertyName("limits")] + public ModelCapabilitiesOverrideLimits? Limits { get; set; } - /// Authentication validation error message. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// Feature flags indicating what the model supports. + [JsonPropertyName("supports")] + public ModelCapabilitiesOverrideSupports? Supports { get; set; } } -/// Identifies the target session. +/// RPC data type for ProtocolSystemMessageAppendConfig operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionGitHubAuthLastAuthErrorsRequest +public sealed class ProtocolSystemMessageAppendConfig { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Text appended to the standard system prompt. + [JsonPropertyName("content")] + public string? Content { get; set; } -/// The enumerated collection, keyed by the same selector as the query. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthEnumerateValueAccounts), "accounts")] -[JsonDerivedType(typeof(AuthEnumerateValueProviders), "providers")] -public partial class AuthEnumerateValue -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Append-mode discriminator. Omission also selects append mode. + [JsonPropertyName("mode")] + public ProtocolAppendMode? Mode { get; set; } } - -/// One signed-in account in the roster forest. +/// RPC data type for SystemMessageBlock operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AccountStatus +public sealed class SystemMessageBlock { - /// Whether this is the active account. - [JsonPropertyName("active")] - public bool Active { get; set; } - - /// Opaque id of the account this one was derived from (e.g. an EMU account's base Entra identity); absent for a root account. Matches the base identity account's selectionId, forming the derivation edge. - [JsonPropertyName("derivedFrom")] - public string? DerivedFrom { get; set; } - - /// Authentication host URL. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; - - /// 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; -} + /// Whether providers with explicit prompt caching should place a cache breakpoint after this block. + [JsonPropertyName("cacheBreakpoint")] + public bool? CacheBreakpoint { get; set; } -/// The accounts variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateValueAccounts : AuthEnumerateValue -{ - /// - [JsonIgnore] - public override string Kind => "accounts"; + /// Text content for this system-message block. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// The signed-in account forest; empty when not logged in. - [JsonPropertyName("items")] - public required IList Items { get; set; } + /// Whether the block is static and may be cached independently of dynamic prompt content. + [JsonPropertyName("isStatic")] + public bool? IsStatic { get; set; } } -/// A provider offered for interactive login. +/// RPC data type for ProtocolSystemMessageReplaceConfig operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderDescriptor +public sealed class ProtocolSystemMessageReplaceConfig { - /// Whether this provider is currently available to sign in with. - [JsonPropertyName("available")] - public bool Available { get; set; } + /// Complete replacement system-message text. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// The neutral provider kind. - [JsonPropertyName("kind")] - public LoginProviderKind Kind { get; set; } + /// Optional structured blocks corresponding to the replacement content. + [JsonPropertyName("contentBlocks")] + public IList? ContentBlocks { 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; + /// Replace-mode discriminator. + [JsonPropertyName("mode")] + public ProtocolReplaceMode Mode { get; set; } } -/// The providers variant of . +/// RPC data type for ProtocolStaticSectionOverride operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateValueProviders : AuthEnumerateValue +public sealed class ProtocolStaticSectionOverride { - /// - [JsonIgnore] - public override string Kind => "providers"; + /// Declarative operation applied to the section. + [JsonPropertyName("action")] + public ProtocolStaticSectionAction Action { get; set; } - /// The providers offered for interactive login. - [JsonPropertyName("items")] - public required IList Items { get; set; } + /// Optional content used by replace, append, and prepend operations. + [JsonPropertyName("content")] + public string? Content { get; set; } } -/// Selects which accounts collection to enumerate. A no-arg selector is the empty-payload variant. -/// Polymorphic base type discriminated by kind. +/// Polymorphic base type discriminated by action. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", + TypeDiscriminatorPropertyName = "action", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthEnumerateQueryAccounts), "accounts")] -[JsonDerivedType(typeof(AuthEnumerateQueryProviders), "providers")] -public partial class AuthEnumerateQuery +[JsonDerivedType(typeof(ProtocolMarkerSectionOverrideTransform), "transform")] +[JsonDerivedType(typeof(ProtocolMarkerSectionOverridePreserve), "preserve")] +public partial class ProtocolMarkerSectionOverride { /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + [JsonPropertyName("action")] + public virtual string Action { get; set; } = string.Empty; } -/// The accounts variant of . +/// The transform variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateQueryAccounts : AuthEnumerateQuery +public partial class ProtocolMarkerSectionOverrideTransform : ProtocolMarkerSectionOverride { /// [JsonIgnore] - public override string Kind => "accounts"; + public override string Action => "transform"; } -/// The providers variant of . +/// The preserve variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthEnumerateQueryProviders : AuthEnumerateQuery +public partial class ProtocolMarkerSectionOverridePreserve : ProtocolMarkerSectionOverride { /// [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; } + public override string Action => "preserve"; } -/// Enumerate request carrying the typed collection query. +/// JSON union data type for ProtocolSectionOverride. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountsEnumerateRequest +[JsonConverter(typeof(Converter))] +public sealed partial class ProtocolSectionOverride { - /// Which typed accounts collection to enumerate. - [JsonPropertyName("query")] - public AuthEnumerateQuery Query { get => field ??= new(); set; } + /// Gets the value when this instance contains . + public ProtocolStaticSectionOverride? ProtocolStaticSectionOverride { get; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Gets the value when this instance contains . + public ProtocolMarkerSectionOverride? ProtocolMarkerSectionOverride { get; } -/// The read result, keyed by the same selector as the query. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthReadValueActiveAccount), "activeAccount")] -[JsonDerivedType(typeof(AuthReadValueStatus), "status")] -[JsonDerivedType(typeof(AuthReadValueLastErrors), "lastErrors")] -public partial class AuthReadValue -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Initializes a new instance of the class from . + public ProtocolSectionOverride(ProtocolStaticSectionOverride value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolStaticSectionOverride = value; + } + /// Converts to . + public static implicit operator ProtocolSectionOverride(ProtocolStaticSectionOverride value) => new(value); -/// The activeAccount variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadValueActiveAccount : AuthReadValue -{ - /// - [JsonIgnore] - public override string Kind => "activeAccount"; + /// Initializes a new instance of the class from . + public ProtocolSectionOverride(ProtocolMarkerSectionOverride value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolMarkerSectionOverride = value; + } - /// The active account, or absent when not logged in. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("account")] - public AccountStatus? Account { get; set; } -} + /// Converts to . + public static implicit operator ProtocolSectionOverride(ProtocolMarkerSectionOverride value) => new(value); -/// Neutral authentication status summary. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthStatusDto -{ - /// Number of signed-in accounts in the roster. - [JsonPropertyName("accountCount")] - public long AccountCount { get; set; } + /// 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."); + } - /// Active account host, if authenticated. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("activeHost")] - public string? ActiveHost { 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); + } - /// Active account login, if authenticated. - [JsonPropertyName("activeLogin")] - public string? ActiveLogin { get; set; } + throw new JsonException("JSON value did not match any ProtocolSectionOverride variant."); + } - /// Copilot plan tier of the active account, if known. - [JsonPropertyName("copilotPlan")] - public string? CopilotPlan { 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; + } - /// Whether the session has resolved authentication. - [JsonPropertyName("isAuthenticated")] - public bool IsAuthenticated { get; set; } + throw new JsonException("No ProtocolSectionOverride variant value is set."); + } + } } -/// The status variant of . +/// RPC data type for ProtocolSystemMessageCustomizeConfig operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadValueStatus : AuthReadValue +public sealed class ProtocolSystemMessageCustomizeConfig { - /// - [JsonIgnore] - public override string Kind => "status"; + /// Text appended after the customized sections. + [JsonPropertyName("content")] + public string? Content { get; set; } - /// The neutral authentication status summary. - [JsonPropertyName("status")] - public required AuthStatusDto Status { get; set; } + /// Customize-mode discriminator. + [JsonPropertyName("mode")] + public ProtocolCustomizeMode Mode { get; set; } + + /// Named standard-prompt section overrides. + [JsonPropertyName("sections")] + public IDictionary? Sections { get; set; } } -/// The lastErrors variant of . +/// JSON union data type for ProtocolSystemMessageConfig. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadValueLastErrors : AuthReadValue +[JsonConverter(typeof(Converter))] +public sealed partial class ProtocolSystemMessageConfig { - /// - [JsonIgnore] - public override string Kind => "lastErrors"; + /// Gets the value when this instance contains . + public ProtocolSystemMessageAppendConfig? ProtocolSystemMessageAppendConfig { get; } - /// Validation errors from the most recent authentication attempt. - [JsonPropertyName("errors")] - public required IList Errors { get; set; } -} + /// Gets the value when this instance contains . + public ProtocolSystemMessageReplaceConfig? ProtocolSystemMessageReplaceConfig { get; } -/// 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; + /// Gets the value when this instance contains . + public ProtocolSystemMessageCustomizeConfig? ProtocolSystemMessageCustomizeConfig { get; } + + /// Initializes a new instance of the class from . + public ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolSystemMessageAppendConfig = value; + } + + /// Converts to . + public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) => new(value); + + /// Initializes a new instance of the class from . + public ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolSystemMessageReplaceConfig = value; + } + + /// Converts to . + public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) => new(value); + + /// Initializes a new instance of the class from . + public ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) + { + ArgumentNullException.ThrowIfNull(value); + ProtocolSystemMessageCustomizeConfig = value; + } + + /// Converts to . + public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) => new(value); + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ProtocolSystemMessageConfig Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + throw new JsonException("Expected JSON object for ProtocolSystemMessageConfig."); + } + + using var document = JsonDocument.ParseValue(ref reader); + var element = document.RootElement; + if (element.ValueKind == JsonValueKind.Object && (!element.TryGetProperty("mode", out _) || (element.TryGetProperty("mode", out _) && element.GetProperty("mode").ValueKind == JsonValueKind.String && (element.GetProperty("mode").GetString() == "append")))) + { + var protocolSystemMessageAppendConfig = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolSystemMessageAppendConfig); + return protocolSystemMessageAppendConfig is null ? throw new JsonException("Expected ProtocolSystemMessageAppendConfig value.") : new ProtocolSystemMessageConfig(protocolSystemMessageAppendConfig); + } + if (element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("mode", out _) && element.GetProperty("mode").ValueKind == JsonValueKind.String && (element.GetProperty("mode").GetString() == "replace"))) + { + var protocolSystemMessageReplaceConfig = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolSystemMessageReplaceConfig); + return protocolSystemMessageReplaceConfig is null ? throw new JsonException("Expected ProtocolSystemMessageReplaceConfig value.") : new ProtocolSystemMessageConfig(protocolSystemMessageReplaceConfig); + } + if (element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("mode", out _) && element.GetProperty("mode").ValueKind == JsonValueKind.String && (element.GetProperty("mode").GetString() == "customize"))) + { + var protocolSystemMessageCustomizeConfig = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolSystemMessageCustomizeConfig); + return protocolSystemMessageCustomizeConfig is null ? throw new JsonException("Expected ProtocolSystemMessageCustomizeConfig value.") : new ProtocolSystemMessageConfig(protocolSystemMessageCustomizeConfig); + } + + throw new JsonException("JSON value did not match any ProtocolSystemMessageConfig variant."); + } + + /// + public override void Write(Utf8JsonWriter writer, ProtocolSystemMessageConfig value, JsonSerializerOptions options) + { + if (value.ProtocolSystemMessageAppendConfig is { } protocolSystemMessageAppendConfig) + { + JsonSerializer.Serialize(writer, protocolSystemMessageAppendConfig, RpcJsonContext.Default.ProtocolSystemMessageAppendConfig); + return; + } + if (value.ProtocolSystemMessageReplaceConfig is { } protocolSystemMessageReplaceConfig) + { + JsonSerializer.Serialize(writer, protocolSystemMessageReplaceConfig, RpcJsonContext.Default.ProtocolSystemMessageReplaceConfig); + return; + } + if (value.ProtocolSystemMessageCustomizeConfig is { } protocolSystemMessageCustomizeConfig) + { + JsonSerializer.Serialize(writer, protocolSystemMessageCustomizeConfig, RpcJsonContext.Default.ProtocolSystemMessageCustomizeConfig); + return; + } + + throw new JsonException("No ProtocolSystemMessageConfig variant value is set."); + } + } } +/// A BYOK model definition referencing a named provider. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderModelConfig +{ + /// 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; -/// The activeAccount variant of . + /// Maximum context window tokens for the model. + [JsonPropertyName("maxContextWindowTokens")] + public double? MaxContextWindowTokens { get; set; } + + /// Maximum output tokens for the model. + [JsonPropertyName("maxOutputTokens")] + public double? MaxOutputTokens { get; set; } + + /// Maximum prompt/input tokens for the model. + [JsonPropertyName("maxPromptTokens")] + public double? MaxPromptTokens { get; set; } + + /// Provider-published model metadata, preserved verbatim as the public Model.metadata object. + [JsonPropertyName("metadata")] + public IDictionary? Metadata { get; set; } + + /// Well-known base model id used for behavior/capability/config lookup. Defaults to `id`. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } + + /// Display name for model pickers. Defaults to the provider-qualified selection id (`provider/id`). + [JsonPropertyName("name")] + public string? Name { get; set; } + + /// Name of the configured provider that serves this model. + [JsonPropertyName("provider")] + public string Provider { get; set; } = string.Empty; + + /// System-message configuration used when the runtime builds the standard prompt for this provider-qualified model, including general-purpose subagents. It uses the same object hierarchy as session-level systemMessage configuration, except transform actions are rejected because the current callback protocol is not model-scoped. When present, it overrides the session-wide configuration on those prompt paths. Selected custom-agent and specialized-subagent prompts remain authoritative. + [JsonPropertyName("systemMessage")] + public ProtocolSystemMessageConfig? SystemMessage { get; set; } + + /// The model name sent to the provider API for inference. Defaults to `id`. + [JsonPropertyName("wireModel")] + public string? WireModel { get; set; } +} + +/// Azure-specific provider options. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadQueryActiveAccount : AuthReadQuery +public sealed class ProviderConfigAzure { - /// - [JsonIgnore] - public override string Kind => "activeAccount"; + /// API version. When set, uses the versioned deployment route. When omitted, uses the GA versionless v1 route. + [JsonPropertyName("apiVersion")] + public string? ApiVersion { get; set; } } -/// The status variant of . +/// 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 partial class AuthReadQueryStatus : AuthReadQuery +public sealed class NamedProviderConfig { - /// - [JsonIgnore] - public override string Kind => "status"; + /// 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; } } -/// The lastErrors variant of . +/// Provider configuration prepared from a discovered model. Preparing a plan changes nothing: it neither registers the model with the session nor writes durable configuration. To apply it, pass `provider` and `model` to `session.provider.add`, omitting whichever the dispositions report as already configured. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthReadQueryLastErrors : AuthReadQuery +public sealed class ModelProviderConfigurationPlan { - /// - [JsonIgnore] - public override string Kind => "lastErrors"; + /// 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; } } -/// Read request carrying the typed datum query. +/// 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 AccountsGetRequest +internal sealed class ModelProviderPrepareConfigurationRequest { - /// Which typed accounts datum to read. - [JsonPropertyName("query")] - public AuthReadQuery Query { get => field ??= new(); set; } + /// 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; } -/// Result of a non-interactive accounts mutation. +/// Managed sandbox enforcement state for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthWriteResult +public sealed class SandboxEnforcementStatus { - /// For a logout, whether other signed-in accounts remain. - [JsonPropertyName("moreUsers")] - public bool? MoreUsers { get; set; } + /// Whether an enforcement failure has permanently blocked the session. + [JsonPropertyName("blocked")] + public bool Blocked { get; set; } - /// Whether the mutation was applied. - [JsonPropertyName("ok")] - public bool Ok { get; set; } + /// The first sandbox enforcement failure that blocked the session. + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// Whether the effective managed policy requires an available sandbox backend. + [JsonPropertyName("required")] + public bool Required { get; set; } } -/// One non-interactive accounts mutation command (the selector is fused with its typed args). -/// Polymorphic base type discriminated by kind. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(AuthWriteSwitchActive), "switchActive")] -[JsonDerivedType(typeof(AuthWriteLogout), "logout")] -[JsonDerivedType(typeof(AuthWriteSetCredentials), "setCredentials")] -public partial class AuthWrite +internal sealed class SessionSandboxGetEnforcementStatusRequest { - /// 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; +} + +/// Result of attempting to disable sandboxing for the current session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SandboxDisableForSessionResult +{ + /// 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; } } +/// 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 PermissionDecisionContext +{ + /// Disposition of the permission request as observed by the responding client. + [JsonPropertyName("outcome")] + public PermissionDecisionOutcome Outcome { get; set; } + + /// Whether the responding client could ask a user interactively, was running headlessly, or had no response path. Omit when the client cannot determine this authoritatively. + [JsonPropertyName("responseCapability")] + public PermissionResponseCapability? ResponseCapability { get; set; } -/// The switchActive variant of . + /// Controlled reason or actor responsible for the response. + [JsonPropertyName("source")] + public PermissionDecisionSource Source { get; set; } + + /// Client surface that submitted the response. + [JsonPropertyName("surface")] + public PermissionDecisionSurface Surface { get; set; } +} + +/// Request to disable sandboxing for the current session while resolving an active sandbox-bypass permission prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthWriteSwitchActive : AuthWrite +internal sealed class SandboxDisableForSessionRequest { - /// - [JsonIgnore] - public override string Kind => "switchActive"; + /// Optional attribution for the permission decision. + [JsonPropertyName("decisionContext")] + public PermissionDecisionContext? DecisionContext { get; set; } - /// Opaque selection id of the account to make active. - [JsonPropertyName("selectionId")] - public required string SelectionId { 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; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// The logout variant of . +/// Result of accepting a sandbox path grant. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthWriteLogout : AuthWrite +public sealed class SandboxGrantPathForRequestResult { - /// - [JsonIgnore] - public override string Kind => "logout"; + /// Whether this call resolved the pending request and added the path to the session's sandbox policy. + [JsonPropertyName("success")] + public bool Success { 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; } +/// Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SandboxGrantPathForRequestRequest +{ + /// Optional attribution for the permission decision. + [JsonPropertyName("decisionContext")] + public PermissionDecisionContext? DecisionContext { get; set; } + + /// Identifier of the exact pending sandbox escalation permission request whose sandboxPathGrant to accept. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// The setCredentials variant of . +/// Authentication status and account metadata for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthWriteSetCredentials : AuthWrite +public sealed class SessionAuthStatus { - /// - [JsonIgnore] - public override string Kind => "setCredentials"; + /// Authentication type. + [JsonPropertyName("authType")] + public AuthInfoType? AuthType { get; set; } + + /// Copilot plan tier (e.g., individual_pro, business). + [JsonPropertyName("copilotPlan")] + public string? CopilotPlan { get; set; } /// Authentication host URL. [Url] [StringSyntax(StringSyntaxAttribute.Uri)] [JsonPropertyName("host")] - public required string Host { get; set; } + public string? Host { get; set; } - /// Login/username for the credential. + /// Whether the session has resolved authentication. + [JsonPropertyName("isAuthenticated")] + public bool IsAuthenticated { get; set; } + + /// Authenticated login/username, if available. [JsonPropertyName("login")] - public required string Login { get; set; } + public string? Login { get; set; } - /// GitHub authentication token to install. - [JsonPropertyName("token")] - public required string Token { get; set; } + /// Human-readable authentication status description. + [JsonPropertyName("statusMessage")] + public string? StatusMessage { get; set; } } -/// Mutation request carrying the typed write command. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AccountsSetRequest +internal sealed class SessionGitHubAuthGetStatusRequest { - /// 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; } -/// 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. +/// Indicates whether the credential update succeeded. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionSetCredentialsResult +{ + /// Whether the session ended up with a populated `copilotUser` for the installed credentials. `true` when the supplied credential already carried `copilotUser` or it was successfully re-resolved server-side. `false` when the credential is installed without `copilotUser` — either re-resolution failed, or the variant cannot be re-resolved from the credential alone (only the raw-token variants `token`, `env`, and `gh-cli` can). In both `false` cases the token swap still applied, but plan/quota/billing metadata is degraded. Present whenever a credential was supplied; omitted only when no credential was supplied (no-op call). + [JsonPropertyName("copilotUserResolved")] + public bool? CopilotUserResolved { get; set; } + + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} + +/// Authentication credentials accepted by session.gitHubAuth.setCredentials. Session-owned token-provider identities cannot be installed through this method. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", + TypeDiscriminatorPropertyName = "type", 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(SettableAuthInfoHmac), "hmac")] +[JsonDerivedType(typeof(SettableAuthInfoEnv), "env")] +[JsonDerivedType(typeof(SettableAuthInfoToken), "token")] +[JsonDerivedType(typeof(SettableAuthInfoCopilotApiToken), "copilot-api-token")] +[JsonDerivedType(typeof(SettableAuthInfoUser), "user")] +[JsonDerivedType(typeof(SettableAuthInfoAccount), "account")] +[JsonDerivedType(typeof(SettableAuthInfoGhCli), "gh-cli")] +[JsonDerivedType(typeof(SettableAuthInfoApiKey), "api-key")] +public partial class SettableAuthInfo { /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// The open-url variant of . +/// Authentication-info input variant for GitHub-internal HMAC auth, carrying the public GitHub host and HMAC secret. +/// The hmac variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepOpenUrl : AuthLoginStep +public partial class SettableAuthInfoHmac : SettableAuthInfo { /// [JsonIgnore] - public override string Kind => "open-url"; + public override string Type => "hmac"; - /// Authorize URL the consumer should open in a browser (consumer-driven browser-open). - [JsonPropertyName("url")] - public required string Url { 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 input-required variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepInputRequired : AuthLoginStep -{ - /// - [JsonIgnore] - public override string Kind => "input-required"; + /// HMAC secret used to sign requests. + [JsonPropertyName("hmac")] + public required string Hmac { get; set; } - /// 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; } + /// Authentication host. HMAC auth always targets the public GitHub host. + [JsonPropertyName("host")] + public required string Host { get; set; } } -/// The awaiting variant of . +/// Authentication-info input variant for a token sourced from an environment variable, with host, optional login, token, and env var name. +/// The env variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepAwaiting : AuthLoginStep +public partial class SettableAuthInfoEnv : SettableAuthInfo { /// [JsonIgnore] - public override string Kind => "awaiting"; -} + public override string Type => "env"; -/// The needs-interaction variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepNeedsInteraction : AuthLoginStep -{ - /// - [JsonIgnore] - public override string Kind => "needs-interaction"; -} + /// 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; } -/// Terminal result of an interactive login flow. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthLoginResultDto -{ - /// Host that was signed in, when completed. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] + /// 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 string? Host { get; set; } + public required string Host { get; set; } - /// Login that was signed in, when completed. + /// 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; } - /// Terminal disposition of the login. - [JsonPropertyName("status")] - public AuthLoginResultStatus Status { get; set; } + /// The token value itself. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// The completed variant of . +/// Token authentication accepted by session.gitHubAuth.setCredentials. +/// The token variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepCompleted : AuthLoginStep +public partial class SettableAuthInfoToken : SettableAuthInfo { /// [JsonIgnore] - public override string Kind => "completed"; + public override string Type => "token"; - /// The terminal login result. - [JsonPropertyName("result")] - public required AuthLoginResultDto Result { 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; } + + /// The token value itself. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// The error variant of . +/// Authentication-info variant for direct Copilot API token auth sourced from environment variables, with public GitHub host. +/// The copilot-api-token variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class AuthLoginStepError : AuthLoginStep +public partial class SettableAuthInfoCopilotApiToken : SettableAuthInfo { /// [JsonIgnore] - public override string Kind => "error"; - - /// Human-readable failure message. - [JsonPropertyName("message")] - public required string Message { get; set; } -} - -/// A started login flow: its opaque id and first step. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthLoginBegun -{ - /// Opaque flow id used to advance or cancel this login. - [JsonPropertyName("flowId")] - public string FlowId { get; set; } = string.Empty; - - /// The first step of the flow. - [JsonPropertyName("step")] - public AuthLoginStep Step { get => field ??= new(); set; } -} - -/// Begin an interactive login flow for a provider kind. Dispatch is kind-only. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AuthLoginBeginRequest -{ - /// The provider kind to sign in with. - [JsonPropertyName("kind")] - public LoginProviderKind Kind { get; set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} - -/// Advance an in-flight login flow, optionally fulfilling an input-required step. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AuthLoginAdvanceRequest -{ - /// Opaque flow id from begin. - [JsonPropertyName("flowId")] - public string FlowId { get; set; } = string.Empty; - - /// 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; -} - -/// Cancel an in-flight login flow. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AuthLoginCancelRequest -{ - /// Opaque flow id from begin. - [JsonPropertyName("flowId")] - public string FlowId { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} - -/// A file included in the session debug bundle. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsCollectedEntry -{ - /// 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; } - - /// Source category for this entry. - [JsonPropertyName("source")] - public DebugCollectLogsSource Source { get; set; } -} - -/// An optional debug bundle entry that could not be included. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsSkippedEntry -{ - /// Relative path requested for this bundle entry. - [JsonPropertyName("bundlePath")] - public string BundlePath { get; set; } = string.Empty; + public override string Type => "copilot-api-token"; - /// Server-local source path that could not be read. - [JsonPropertyName("path")] - public string? Path { 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; } - /// Reason the entry was skipped. - [JsonPropertyName("reason")] - public string Reason { get; set; } = string.Empty; + /// Authentication host (always the public GitHub host). + [JsonPropertyName("host")] + public required string Host { get; set; } } -/// Result of collecting a session debug bundle. +/// 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 DebugCollectLogsResult +public partial class SettableAuthInfoUser : SettableAuthInfo { - /// Files included in the bundle. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Type => "user"; - /// Destination kind that was written. - [JsonPropertyName("kind")] - public DebugCollectLogsResultKind Kind { 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; } - /// 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; + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Optional files or directories that could not be included. - [JsonPropertyName("skippedEntries")] - public IList? SkippedEntries { get; set; } + /// OAuth user login. + [JsonPropertyName("login")] + public required string Login { get; set; } } -/// A caller-provided server-local file or directory to include in the debug bundle. +/// 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 sealed class DebugCollectLogsEntry +public partial class SettableAuthInfoAccount : SettableAuthInfo { - /// 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; } - - /// Server-local source path to read. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; - - /// How text content from this entry should be redacted. Defaults to plain-text. With none, no redaction is applied; the caller must ensure any necessary redaction is performed before this call. - [JsonPropertyName("redaction")] - public DebugCollectLogsRedaction? Redaction { get; set; } + /// + [JsonIgnore] + public override string Type => "account"; - /// 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; } -} + /// Host coordinate owned by the account's model provider. + [JsonPropertyName("host")] + public required string Host { get; set; } -/// Destination for the session debug bundle. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(DebugCollectLogsDestinationArchive), "archive")] -[JsonDerivedType(typeof(DebugCollectLogsDestinationDirectory), "directory")] -public partial class DebugCollectLogsDestination -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// Login identifying the provider-owned account. + [JsonPropertyName("login")] + public required string Login { get; set; } } - -/// The archive variant of . +/// Authentication-info input variant for GitHub CLI credentials, carrying host, login, and the `gh auth token` value. +/// The gh-cli variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class DebugCollectLogsDestinationArchive : DebugCollectLogsDestination +public partial class SettableAuthInfoGhCli : SettableAuthInfo { /// [JsonIgnore] - public override string Kind => "archive"; + public override string Type => "gh-cli"; - /// When true, create the archive atomically without overwriting an existing file by appending ` (N)` before the extension as needed. Defaults to false. + /// 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("noOverwrite")] - public bool? NoOverwrite { get; set; } + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Absolute or server-relative path for the .tgz archive to create. - [JsonPropertyName("outputPath")] - public required string OutputPath { get; set; } -} + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } -/// The directory variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class DebugCollectLogsDestinationDirectory : DebugCollectLogsDestination -{ - /// - [JsonIgnore] - public override string Kind => "directory"; + /// User login as reported by `gh auth status`. + [JsonPropertyName("login")] + public required string Login { get; set; } - /// Directory where files should be staged. The directory is created if needed. - [JsonPropertyName("outputDirectory")] - public required string OutputDirectory { get; set; } + /// The token returned by `gh auth token`. Treat as a secret. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// Built-in session diagnostics to include in the bundle. Omitted fields default to true. +/// Authentication-info input variant for API-key authentication to a non-GitHub LLM provider, carrying the secret `apiKey` and host. +/// The api-key variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DebugCollectLogsInclude +public partial class SettableAuthInfoApiKey : SettableAuthInfo { - /// 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; } - - /// Include the session event log (`events.jsonl`). Defaults to true. - [JsonPropertyName("events")] - public bool? Events { get; set; } - - /// Server-local path to the session's events.jsonl file. Internal callers normally omit this and let the runtime derive it from the session. - [JsonPropertyName("eventsPath")] - public string? EventsPath { get; set; } - - /// Maximum number of previous process logs to include. Defaults to 5. - [JsonPropertyName("previousProcessLogLimit")] - public long? PreviousProcessLogLimit { get; set; } + /// + [JsonIgnore] + public override string Type => "api-key"; - /// 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; } + /// The API key. Treat as a secret. + [JsonPropertyName("apiKey")] + public required string ApiKey { get; set; } - /// Include process logs for the session. Defaults to true. - [JsonPropertyName("processLogs")] - public bool? ProcessLogs { 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; } - /// Include interactive shell logs written under the session's `shell-logs` directory. Defaults to true. - [JsonPropertyName("shellLogs")] - public bool? ShellLogs { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public required string Host { get; set; } } -/// Options for collecting a session debug bundle with configurable redaction. +/// New auth credentials to install on the session. Omit to leave credentials unchanged. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DebugCollectLogsRequest +internal sealed class SessionSetCredentialsParams { - /// 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; } - - /// 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; } - - /// Which built-in session diagnostics to include. Omitted fields default to true. - [JsonPropertyName("include")] - public DebugCollectLogsInclude? Include { get; set; } + /// 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; } -/// 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; } - - /// JSON Schema for the action input. - [JsonPropertyName("inputSchema")] - public JsonElement? InputSchema { get; set; } - - /// Action name exposed by the canvas provider. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} - -/// Canvas available in the current session. +/// Credential-free authentication identity safe to expose to hosts and user interfaces. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiscoveredCanvas +public sealed class AuthIdentity { - /// Actions the agent or host may invoke on an open instance. - [JsonPropertyName("actions")] - public IList? Actions { get; set; } - - /// Provider-local canvas identifier. - [JsonPropertyName("canvasId")] - public string CanvasId { get; set; } = string.Empty; - - /// Short, single-sentence description shown to the agent in canvas catalogs. - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; - - /// Human-readable canvas name. - [JsonPropertyName("displayName")] - public string DisplayName { get; set; } = string.Empty; + /// Snapshot of the authenticated user's Copilot subscription info, if known. + [JsonPropertyName("copilotUser")] + public CopilotUserResponse? CopilotUser { get; set; } - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; + /// Name of the environment variable that supplied the credential, when applicable. + [JsonPropertyName("envVar")] + public string? EnvVar { get; set; } - /// Owning extension display name, when available. - [JsonPropertyName("extensionName")] - public string? ExtensionName { get; set; } + /// Authentication host. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Host-local PNG path for the canvas icon, when supplied. - [JsonPropertyName("icon")] - public string? Icon { get; set; } + /// Authenticated login, when available. + [JsonPropertyName("login")] + public string? Login { get; set; } - /// JSON Schema for canvas open input. - [JsonPropertyName("inputSchema")] - public JsonElement? InputSchema { get; set; } -} + /// Opaque SDK GitHub credential registration backing this identity. Routing metadata only; never a credential. + [JsonPropertyName("registrationId")] + public string? RegistrationId { 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; } + /// Authentication type. + [JsonPropertyName("type")] + public AuthInfoType Type { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionCanvasListRequest +internal sealed class SessionGitHubAuthGetCurrentAuthInfoRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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; - - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; - - /// Owning extension display name, when available. - [JsonPropertyName("extensionName")] - public string? ExtensionName { get; set; } - - /// Host-local PNG path for the canvas icon, when supplied. - [JsonPropertyName("icon")] - public string? Icon { get; set; } - - /// Input supplied when the instance was opened. - [JsonPropertyName("input")] - public JsonElement? Input { get; set; } - - /// Stable caller-supplied canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; - - /// Provider-supplied status text. - [JsonPropertyName("status")] - public string? Status { get; set; } - - /// Rendered title. - [JsonPropertyName("title")] - public string? Title { get; set; } - - /// URL for web-rendered canvases. - [JsonPropertyName("url")] - public string? Url { get; set; } -} - -/// Live open-canvas snapshot. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasListOpenResult +internal sealed class SessionGitHubAuthGetAllAuthAvailableRequest { - /// Currently open canvas instances. - [JsonPropertyName("openCanvases")] - public IList OpenCanvases { get => field ??= []; 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 SessionCanvasListOpenRequest +internal sealed class SessionGitHubAuthRefreshCopilotUserRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Canvas open parameters. +/// Internal GitHub login parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasOpenRequest +internal sealed class SessionAuthLoginRequest { - /// 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; } + /// GitHub host URL. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Canvas open input. - [JsonPropertyName("input")] - public JsonElement? Input { get; set; } + /// GitHub login. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Caller-supplied stable instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// 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; } -/// Canvas close parameters. +/// Parameters for switching the session's active authentication. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasCloseRequest +internal sealed class SessionAuthSwitchRequest { - /// Open canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// 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; -} -/// Canvas action invocation result. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasActionInvokeResult -{ - /// Provider-supplied action result. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// Optional token paired with the authentication information. + [JsonPropertyName("token")] + public string? Token { get; set; } } -/// Canvas action invocation parameters. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasActionInvokeRequest +internal sealed class SessionGitHubAuthLogoutRequest { - /// 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; } -/// Internal canvas provider registration parameters. +/// Parameters identifying a GitHub authentication to log out. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasProviderRegisterRequest +internal sealed class SessionAuthLogoutUserRequest { - /// 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; } + /// 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; } -/// Internal canvas provider unregistration parameters. +/// Validation error from an authentication attempt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CanvasProviderUnregisterRequest +public sealed class AuthValidationError { - /// Connection identifier to unregister. - [JsonPropertyName("connectionId")] - public string ConnectionId { 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; +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionGitHubAuthLastAuthErrorsRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Machine-readable workflow run failure. -/// Polymorphic base type discriminated by type. +/// 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 = "type", + TypeDiscriminatorPropertyName = "kind", 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 +[JsonDerivedType(typeof(AuthEnumerateValueAccounts), "accounts")] +[JsonDerivedType(typeof(AuthEnumerateValueProviders), "providers")] +public partial class AuthEnumerateValue { /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// The workflow_limit_reached variant of . +/// One signed-in account in the roster forest. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowLimitReached : WorkflowRunFailure +public sealed class AccountStatus { - /// - [JsonIgnore] - public override string Type => "workflow_limit_reached"; - - /// Resource ceiling that stopped the run. - [JsonPropertyName("kind")] - public required WorkflowRunFailureKind Kind { get; set; } - - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } + /// Whether this is the active account. + [JsonPropertyName("active")] + public bool Active { get; set; } - /// Suggested larger ceiling when the runtime can derive one safely. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("suggestedValue")] - public double? SuggestedValue { 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; } - /// Approved effective ceiling that was reached. - [JsonPropertyName("value")] - public required double Value { get; set; } -} + /// Authentication host URL. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; -/// The workflow_resume_declined variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowResumeDeclined : WorkflowRunFailure -{ - /// - [JsonIgnore] - public override string Type => "workflow_resume_declined"; + /// The provider kind of this account. + [JsonPropertyName("kind")] + public AccountKind Kind { get; set; } - /// Human-readable reason the resume did not proceed. - [JsonPropertyName("reason")] - public required string Reason { get; set; } + /// Authenticated login/username. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Workflow run identifier whose changed limits were declined. - [JsonPropertyName("runId")] - public required string RunId { get; set; } + /// Opaque selection id used to switch to, or log out, this account. + [JsonPropertyName("selectionId")] + public string SelectionId { get; set; } = string.Empty; } -/// The workflow_durable_failure variant of . +/// The accounts variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowDurableFailure : WorkflowRunFailure +public partial class AuthEnumerateValueAccounts : AuthEnumerateValue { /// [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; } + public override string Kind => "accounts"; - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } + /// The signed-in account forest; empty when not logged in. + [JsonPropertyName("items")] + public required IList Items { get; set; } } -/// The run stopped because its usage accounting could not be completed. -/// The workflow_accounting_incomplete variant of . +/// A provider offered for interactive login. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowAccountingIncomplete : WorkflowRunFailure +public sealed class ProviderDescriptor { - /// - [JsonIgnore] - public override string Type => "workflow_accounting_incomplete"; + /// Whether this provider is currently available to sign in with. + [JsonPropertyName("available")] + public bool Available { get; set; } - /// Confirmed usage in nano-AIU, representing the floor of what the run spent. - [JsonPropertyName("drainedNanoAiu")] - public required long DrainedNanoAiu { get; set; } + /// The neutral provider kind. + [JsonPropertyName("kind")] + public LoginProviderKind Kind { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } + /// Human-readable menu label, owned by the runtime so every consumer renders identical text. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; } -/// The 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 . +/// The providers variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowRunFailureWorkflowProviderDisconnected : WorkflowRunFailure +public partial class AuthEnumerateValueProviders : AuthEnumerateValue { /// [JsonIgnore] - public override string Type => "workflow_provider_disconnected"; + public override string Kind => "providers"; - /// Workflow run identifier. - [JsonPropertyName("runId")] - public required string RunId { get; set; } + /// The providers offered for interactive login. + [JsonPropertyName("items")] + public required IList Items { get; set; } } -/// Durable metadata describing who initiated a workflow pause. -/// Polymorphic base type discriminated by type. +/// 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 = "type", + TypeDiscriminatorPropertyName = "kind", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(WorkflowPauseInfoUser), "user")] -[JsonDerivedType(typeof(WorkflowPauseInfoCheckpoint), "checkpoint")] -public partial class WorkflowPauseInfo +[JsonDerivedType(typeof(AuthEnumerateQueryAccounts), "accounts")] +[JsonDerivedType(typeof(AuthEnumerateQueryProviders), "providers")] +public partial class AuthEnumerateQuery { /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// The user variant of . +/// The accounts variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowPauseInfoUser : WorkflowPauseInfo +public partial class AuthEnumerateQueryAccounts : AuthEnumerateQuery { /// [JsonIgnore] - public override string Type => "user"; + public override string Kind => "accounts"; } -/// The checkpoint variant of . +/// The providers variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class WorkflowPauseInfoCheckpoint : WorkflowPauseInfo +public partial class AuthEnumerateQueryProviders : AuthEnumerateQuery { /// [JsonIgnore] - public override string Type => "checkpoint"; + public override string Kind => "providers"; - /// Stable author-defined checkpoint key that initiated the pause. - [JsonPropertyName("key")] - public required string Key { 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; } } -/// Complete current or terminal workflow run envelope. +/// Enumerate request carrying the typed collection query. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunResult +internal sealed class AccountsEnumerateRequest { - /// 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; } - - /// Error message for an errored run. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Machine-readable failure details for a halted or errored run. - [JsonPropertyName("failure")] - public WorkflowRunFailure? Failure { get; set; } + /// Which typed accounts collection to enumerate. + [JsonPropertyName("query")] + public AuthEnumerateQuery Query { get => field ??= new(); set; } - /// Structured pause initiator metadata for a paused attempt. - [JsonPropertyName("pauseInfo")] - public WorkflowPauseInfo? PauseInfo { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Reason for a halted or cancelled run. - [JsonPropertyName("reason")] - public string? Reason { get; set; } +/// The read result, keyed by the same selector as the query. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthReadValueActiveAccount), "activeAccount")] +[JsonDerivedType(typeof(AuthReadValueStatus), "status")] +[JsonDerivedType(typeof(AuthReadValueLastErrors), "lastErrors")] +public partial class AuthReadValue +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Completed workflow result. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; +/// The activeAccount variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthReadValueActiveAccount : AuthReadValue +{ + /// + [JsonIgnore] + public override string Kind => "activeAccount"; - /// Partial journal and progress snapshot for a halted, cancelled, or errored run. - [JsonPropertyName("snapshot")] - public JsonElement? Snapshot { get; set; } + /// The active account, or absent when not logged in. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("account")] + public AccountStatus? Account { get; set; } - /// Current or terminal workflow run status. - [JsonPropertyName("status")] - public WorkflowRunStatus Status { 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; } } -/// Wire-only per-invocation workflow resource ceiling overrides. +/// Neutral authentication status summary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunLimits +public sealed class AuthStatusDto { - /// 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; } + /// Number of signed-in accounts in the roster. + [JsonPropertyName("accountCount")] + public long AccountCount { get; set; } - /// Maximum number of workflow subagents that may run concurrently. - [JsonPropertyName("maxConcurrentSubagents")] - public long? MaxConcurrentSubagents { get; set; } + /// Active account host, if authenticated. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("activeHost")] + public string? ActiveHost { get; set; } - /// Maximum total number of workflow subagents that may be admitted. - [JsonPropertyName("maxTotalSubagents")] - public long? MaxTotalSubagents { get; set; } + /// Active account login, if authenticated. + [JsonPropertyName("activeLogin")] + public string? ActiveLogin { 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; } + /// Copilot plan tier of the active account, if known. + [JsonPropertyName("copilotPlan")] + public string? CopilotPlan { get; set; } + + /// Whether the session has resolved authentication. + [JsonPropertyName("isAuthenticated")] + public bool IsAuthenticated { get; set; } } -/// Options controlling workflow invocation. +/// The status variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunOptions +public partial class AuthReadValueStatus : AuthReadValue { - /// Per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } + /// + [JsonIgnore] + public override string Kind => "status"; - /// Whether to emit workflow phase names to the session transcript. - [JsonPropertyName("logPhaseNames")] - public bool? LogPhaseNames { get; set; } + /// The neutral authentication status summary. + [JsonPropertyName("status")] + public required AuthStatusDto Status { get; set; } +} - /// Whether to notify the originating session when the workflow completes. - [JsonPropertyName("notifyOnComplete")] - public bool? NotifyOnComplete { get; set; } +/// The lastErrors variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthReadValueLastErrors : AuthReadValue +{ + /// + [JsonIgnore] + public override string Kind => "lastErrors"; - /// Run identifier whose journal and progress should seed this resumed run. - [JsonPropertyName("resumeFromRunId")] - public string? ResumeFromRunId { get; set; } + /// Validation errors from the most recent authentication attempt. + [JsonPropertyName("errors")] + public required IList Errors { get; set; } } -/// Parameters for invoking a registered workflow. +/// Selects which typed accounts datum to read. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowRunRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthReadQueryActiveAccount), "activeAccount")] +[JsonDerivedType(typeof(AuthReadQueryStatus), "status")] +[JsonDerivedType(typeof(AuthReadQueryLastErrors), "lastErrors")] +public partial class AuthReadQuery { - /// Workflow input value. - [JsonPropertyName("args")] - public JsonElement Args { get; set; } - - /// Registered workflow name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Workflow invocation options. - [JsonPropertyName("options")] - public WorkflowRunOptions? Options { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// The activeAccount variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthReadQueryActiveAccount : AuthReadQuery +{ + /// + [JsonIgnore] + public override string Kind => "activeAccount"; } -/// Resolved persisted workflow identity and resumed run envelope. +/// The status variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowResumeResult +public partial class AuthReadQueryStatus : AuthReadQuery { - /// Terminal resumed run envelope. - [JsonPropertyName("run")] - public WorkflowRunResult Run { get => field ??= new(); set; } - - /// Persisted workflow name resolved for the resumed run. - [JsonPropertyName("workflowName")] - public string WorkflowName { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "status"; } -/// Parameters for resuming a workflow run from its persisted identity. +/// The lastErrors variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowResumeRequest +public partial class AuthReadQueryLastErrors : AuthReadQuery { - /// Optional per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } - - /// Whether to emit workflow phase names to the session transcript. - [JsonPropertyName("logPhaseNames")] - public bool? LogPhaseNames { get; set; } - - /// Whether to notify the originating session when the workflow completes. - [JsonPropertyName("notifyOnComplete")] - public bool? NotifyOnComplete { get; set; } + /// + [JsonIgnore] + public override string Kind => "lastErrors"; +} - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; +/// Read request carrying the typed datum query. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +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; } -/// Options for an internal tool-originated workflow invocation. +/// Result of a non-interactive accounts mutation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowToolRunOptions +public sealed class AuthWriteResult { - /// Per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } + /// For a logout, whether other signed-in accounts remain. + [JsonPropertyName("moreUsers")] + public bool? MoreUsers { get; set; } - /// Run identifier whose journal and progress should seed this resumed run. - [JsonPropertyName("resumeFromRunId")] - public string? ResumeFromRunId { get; set; } + /// Whether the mutation was applied. + [JsonPropertyName("ok")] + public bool Ok { get; set; } } -/// Internal parameters for invoking a registered workflow from a tool. +/// 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 WorkflowToolRunRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(AuthWriteSwitchActive), "switchActive")] +[JsonDerivedType(typeof(AuthWriteLogout), "logout")] +[JsonDerivedType(typeof(AuthWriteSetCredentials), "setCredentials")] +public partial class AuthWrite { - /// Workflow input value. - [JsonPropertyName("args")] - public JsonElement Args { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Registered workflow name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - /// Tool-originated workflow invocation options. - [JsonPropertyName("options")] - public WorkflowToolRunOptions? Options { get; set; } +/// The switchActive variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthWriteSwitchActive : AuthWrite +{ + /// + [JsonIgnore] + public override string Kind => "switchActive"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Opaque selection id of the account to make active. + [JsonPropertyName("selectionId")] + public required string SelectionId { get; set; } +} - /// Opaque identifier of the originating tool call. - [JsonPropertyName("toolCallId")] - public string? ToolCallId { get; set; } +/// The logout variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthWriteLogout : AuthWrite +{ + /// + [JsonIgnore] + public override string Kind => "logout"; + + /// Opaque selection id of the account to log out; absent logs out the active account. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("selectionId")] + public string? SelectionId { get; set; } } -/// Internal parameters for resuming a workflow run from a tool. +/// The setCredentials variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowToolResumeRequest +public partial class AuthWriteSetCredentials : AuthWrite { - /// Optional per-invocation resource ceiling overrides. - [JsonPropertyName("limits")] - public WorkflowRunLimits? Limits { get; set; } + /// + [JsonIgnore] + public override string Kind => "setCredentials"; - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Authentication host URL. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public required string Host { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Login/username for the credential. + [JsonPropertyName("login")] + public required string Login { get; set; } - /// Opaque identifier of the originating tool call. - [JsonPropertyName("toolCallId")] - public string? ToolCallId { get; set; } + /// GitHub authentication token to install. + [JsonPropertyName("token")] + public required string Token { get; set; } } -/// Parameters for retrieving a workflow run. +/// Mutation request carrying the typed write command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowGetRunRequest +internal sealed class AccountsSetRequest { - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// The non-interactive mutation command to apply. + [JsonPropertyName("command")] + public AuthWrite Command { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Declared or approved workflow resource ceilings. +/// 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 WorkflowDeclaredLimits +[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 { - /// 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 total subagents spawned by the run. - [JsonPropertyName("maxTotalSubagents")] - public long? MaxTotalSubagents { get; set; } - - /// Maximum accumulated active execution time in seconds. - [JsonPropertyName("timeoutSeconds")] - public double? TimeoutSeconds { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; } -/// Durable workflow resource consumption. + +/// The open-url variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunConsumed +public partial class AuthLoginStepOpenUrl : AuthLoginStep { - /// Accumulated active execution time in milliseconds. - [JsonPropertyName("activeMs")] - public long ActiveMs { get; set; } - - /// AI usage consumed by the run in nano-AIU. - [JsonPropertyName("nanoAiu")] - public long NanoAiu { get; set; } + /// + [JsonIgnore] + public override string Kind => "open-url"; - /// Total subagents spawned by the run. - [JsonPropertyName("subagents")] - public long Subagents { get; set; } + /// Authorize URL the consumer should open in a browser (consumer-driven browser-open). + [JsonPropertyName("url")] + public required string Url { get; set; } } -/// Current workflow phase identity. +/// The input-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowCurrentPhase +public partial class AuthLoginStepInputRequired : AuthLoginStep { - /// Current phase identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "input-required"; - /// Zero-based declared phase ordinal, or null for an undeclared phase. - [JsonPropertyName("ordinal")] - public long? Ordinal { get; set; } + /// 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; } } -/// Prompt-safe terminal workflow outcome. +/// The awaiting variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunTerminal +public partial class AuthLoginStepAwaiting : AuthLoginStep { - /// 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; } - - /// Prompt-safe preview of the completed result. - [JsonPropertyName("resultPreview")] - public string? ResultPreview { get; set; } + /// + [JsonIgnore] + public override string Kind => "awaiting"; } -/// Durable workflow run summary with read-time live overlays. +/// The needs-interaction variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunSummary +public partial class AuthLoginStepNeedsInteraction : AuthLoginStep { - /// 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; } - - /// 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; } + /// + [JsonIgnore] + public override string Kind => "needs-interaction"; +} - /// Number of phases declared by the workflow. - [JsonPropertyName("declaredPhaseCount")] - public long DeclaredPhaseCount { 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; - /// Human-readable workflow description. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Provider kind that owns this account choice. + [JsonPropertyName("kind")] + public AccountKind Kind { get; set; } - /// Number of direct workflow agents currently live. - [JsonPropertyName("liveAgentCount")] - public long LiveAgentCount { get; set; } + /// Human-readable login for the account choice. + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; - /// Epoch milliseconds when this live-overlay snapshot was observed. - [JsonPropertyName("observedAt")] - public long ObservedAt { get; set; } + /// Opaque identifier supplied to the next login step to select this account. + [JsonPropertyName("selectionId")] + public string SelectionId { get; set; } = string.Empty; +} - /// Monotonic durable run revision. - [JsonPropertyName("revision")] - public long Revision { 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; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Host that was signed in, when completed. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("host")] + public string? Host { get; set; } - /// Epoch milliseconds when execution first started, or null before start. - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } + /// Login that was signed in, when completed. + [JsonPropertyName("login")] + public string? Login { get; set; } - /// Current workflow run status. + /// Current disposition of the login, including pending user decisions. [JsonPropertyName("status")] - public WorkflowRunStatus Status { get; set; } + public AuthLoginResultStatus Status { get; set; } +} - /// Terminal run outcome, or null while nonterminal. - [JsonPropertyName("terminal")] - public WorkflowRunTerminal? Terminal { get; set; } +/// The completed variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthLoginStepCompleted : AuthLoginStep +{ + /// + [JsonIgnore] + public override string Kind => "completed"; - /// Total direct workflow agents spawned across all attempts. - [JsonPropertyName("totalSpawnedAgentCount")] - public long TotalSpawnedAgentCount { 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; } +} - /// Epoch milliseconds when the durable run was last updated. - [JsonPropertyName("updatedAt")] - public long UpdatedAt { get; set; } +/// The error variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class AuthLoginStepError : AuthLoginStep +{ + /// + [JsonIgnore] + public override string Kind => "error"; - /// Registered workflow name. - [JsonPropertyName("workflowName")] - public string WorkflowName { get; set; } = string.Empty; + /// Human-readable failure message. + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// A page of workflow runs in durable creation order. +/// A started login flow: its opaque id and first step. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowListRunsResult +public sealed class AuthLoginBegun { - /// Whether terminal runs newer than this page exist. - [JsonPropertyName("hasMoreNewer")] - public bool? HasMoreNewer { 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 flow id used to advance or cancel this login. + [JsonPropertyName("flowId")] + public string FlowId { 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; } + /// The first step of the flow. + [JsonPropertyName("step")] + public AuthLoginStep Step { get => field ??= new(); set; } +} - /// Number of terminal runs older than this page. - [JsonPropertyName("omittedOlder")] - public long? OmittedOlder { get; set; } +/// Begin an interactive login flow for a provider kind. Dispatch is kind-only. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class AuthLoginBeginRequest +{ + /// The provider kind to sign in with. + [JsonPropertyName("kind")] + public LoginProviderKind Kind { get; set; } - /// Workflow run summaries in durable creation order. - [JsonPropertyName("runs")] - public IList Runs { get => field ??= []; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Parameters for paging workflow runs. +/// Advance an in-flight login flow, optionally fulfilling an input-required step. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowListRunsRequest +internal sealed class AuthLoginAdvanceRequest { - /// Exclusive forward cursor. - [JsonPropertyName("afterSeq")] - public long? AfterSeq { get; set; } - - /// Exclusive backward cursor. - [JsonPropertyName("beforeSeq")] - public long? BeforeSeq { get; set; } + /// Opaque flow id from begin. + [JsonPropertyName("flowId")] + public string FlowId { get; set; } = string.Empty; - /// Maximum terminal runs to return. Defaults to 200 and is capped at 500. - [JsonPropertyName("limit")] - public int? Limit { 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; } -/// Prompt-safe durable identity and live status for a direct workflow agent. +/// Cancel an in-flight login flow. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAgentSummary +internal sealed class AuthLoginCancelRequest { - /// Accumulated active agent time in milliseconds. - [JsonPropertyName("activeMs")] - public long ActiveMs { get; set; } - - /// Prompt-safe live activity text. - [JsonPropertyName("activity")] - public string? Activity { get; set; } - - /// Stable direct-agent identifier. - [JsonPropertyName("agentId")] - public string AgentId { get; set; } = string.Empty; + /// Opaque flow id from begin. + [JsonPropertyName("flowId")] + public string FlowId { get; set; } = string.Empty; - /// Registered agent type. - [JsonPropertyName("agentType")] - public string AgentType { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Epoch milliseconds when the agent completed. - [JsonPropertyName("completedAt")] - public long? CompletedAt { get; set; } +/// A file included in the session debug bundle. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DebugCollectLogsCollectedEntry +{ + /// Relative path of the file in the staged bundle/archive. + [JsonPropertyName("bundlePath")] + public string BundlePath { get; set; } = string.Empty; - /// Friendly, non-unique name intended for display. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Redacted output size in bytes. + [JsonPropertyName("sizeBytes")] + public long SizeBytes { get; set; } - /// Friendly, non-unique name intended for display. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; + /// Source category for this entry. + [JsonPropertyName("source")] + public DebugCollectLogsSource Source { get; set; } +} - /// Phase identifier active when the agent was launched, or null. - [JsonPropertyName("phaseId")] - public string? PhaseId { get; set; } +/// An optional debug bundle entry that could not be included. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DebugCollectLogsSkippedEntry +{ + /// Relative path requested for this bundle entry. + [JsonPropertyName("bundlePath")] + public string BundlePath { get; set; } = string.Empty; - /// Model requested when the agent was launched. - [JsonPropertyName("requestedModel")] - public string? RequestedModel { get; set; } + /// Server-local source path that could not be read. + [JsonPropertyName("path")] + public string? Path { get; set; } - /// Concrete model resolved for the agent. - [JsonPropertyName("resolvedModel")] - public string? ResolvedModel { get; set; } + /// Reason the entry was skipped. + [JsonPropertyName("reason")] + public string Reason { get; set; } = string.Empty; +} - /// Owning workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; +/// 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; } - /// Epoch milliseconds when the agent started. - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } + /// Destination kind that was written. + [JsonPropertyName("kind")] + public DebugCollectLogsResultKind Kind { get; set; } - /// Current durable or live agent status. - [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; + /// 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; - /// Tool-call identifier that launched the agent. - [JsonPropertyName("toolCallId")] - public string ToolCallId { get; set; } = string.Empty; + /// Optional files or directories that could not be included. + [JsonPropertyName("skippedEntries")] + public IList? SkippedEntries { get; set; } } -/// Durable lifecycle and timing for one workflow phase. +/// A caller-provided server-local file or directory to include in the debug bundle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowPhaseObservation +public sealed class DebugCollectLogsEntry { - /// 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; } + /// Relative path to use inside the staged bundle/archive. + [JsonPropertyName("bundlePath")] + public string BundlePath { get; set; } = string.Empty; - /// Number of times execution entered this phase. - [JsonPropertyName("entryCount")] - public long EntryCount { get; set; } + /// Kind of source path to include. + [JsonPropertyName("kind")] + public DebugCollectLogsEntryKind Kind { get; set; } - /// Phase identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Server-local source path to read. + [JsonPropertyName("path")] + public string Path { 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; } + /// 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; } - /// Direct agents in this phase that are currently live. - [JsonPropertyName("liveAgentCount")] - public long LiveAgentCount { 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; } +} - /// Zero-based declared phase ordinal, or null for an undeclared phase. - [JsonPropertyName("ordinal")] - public long? Ordinal { get; set; } +/// Destination for the session debug bundle. +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(DebugCollectLogsDestinationArchive), "archive")] +[JsonDerivedType(typeof(DebugCollectLogsDestinationDirectory), "directory")] +public partial class DebugCollectLogsDestination +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// 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; } - /// Derived lifecycle state of the phase. - [JsonPropertyName("status")] - public WorkflowPhaseStatus Status { get; set; } +/// The archive variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class DebugCollectLogsDestinationArchive : DebugCollectLogsDestination +{ + /// + [JsonIgnore] + public override string Kind => "archive"; - /// Human-readable phase title. - [JsonPropertyName("title")] - public string Title { get; set; } = string.Empty; + /// 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; } - /// Total direct agents associated with this phase. - [JsonPropertyName("totalAgentCount")] - public long TotalAgentCount { get; set; } + /// Absolute or server-relative path for the .tgz archive to create. + [JsonPropertyName("outputPath")] + public required string OutputPath { get; set; } } -/// One durable workflow progress record. +/// The directory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowProgressLine +public partial class DebugCollectLogsDestinationDirectory : DebugCollectLogsDestination { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "directory"; - /// Prompt-safe progress text. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; + /// Directory where files should be staged. The directory is created if needed. + [JsonPropertyName("outputDirectory")] + public required string OutputDirectory { get; set; } } -/// A bidirectional page of workflow progress. +/// Built-in session diagnostics to include in the bundle. Omitted fields default to true. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowProgressPage +public sealed class DebugCollectLogsInclude { - /// Whether progress records newer than this page exist. - [JsonPropertyName("hasMoreNewer")] - public bool HasMoreNewer { get; set; } + /// Server-local path to the current process log. When set, it is included as `process.log` and its directory is searched for prior logs from the same session. + [JsonPropertyName("currentProcessLogPath")] + public string? CurrentProcessLogPath { get; set; } - /// Whether progress records older than this page exist. - [JsonPropertyName("hasMoreOlder")] - public bool HasMoreOlder { get; set; } + /// Include the session event log (`events.jsonl`). Defaults to true. + [JsonPropertyName("events")] + public bool? Events { get; set; } - /// Newest sequence number in this page, or null when empty. - [JsonPropertyName("newestSeq")] - public long? NewestSeq { 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; } - /// Oldest sequence number in this page, or null when empty. - [JsonPropertyName("oldestSeq")] - public long? OldestSeq { get; set; } + /// Maximum number of previous process logs to include. Defaults to 5. + [JsonPropertyName("previousProcessLogLimit")] + public long? PreviousProcessLogLimit { get; set; } - /// Progress records in sequence order. - [JsonPropertyName("records")] - public IList Records { get => field ??= []; 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; } - /// Run revision reflected by this page. - [JsonPropertyName("revision")] - public long Revision { 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; } } -/// Full workflow run observability detail. +/// Options for collecting a session debug bundle with configurable redaction. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowRunDetail +internal sealed class DebugCollectLogsRequest { - /// Epoch milliseconds when the current active segment started, or null while inactive. - [JsonPropertyName("activeSegmentStartedAt")] - public long? ActiveSegmentStartedAt { 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; } - /// 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; } + /// 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 the durable run state currently passes runtime resume eligibility checks. - [JsonPropertyName("canResume")] - public bool CanResume { get; set; } + /// Which built-in session diagnostics to include. Omitted fields default to true. + [JsonPropertyName("include")] + public DebugCollectLogsInclude? Include { get; set; } - /// Epoch milliseconds when the run completed, or null while nonterminal. - [JsonPropertyName("completedAt")] - public long? CompletedAt { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Durable resource consumption. - [JsonPropertyName("consumed")] - public WorkflowRunConsumed Consumed { get => field ??= new(); set; } +/// Canvas action that the agent or host can invoke. To discover the input schema for a particular action, call the list_canvas_capabilities tool. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CanvasAction +{ + /// Description of the action. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Epoch milliseconds when the run was created. - [JsonPropertyName("createdAt")] - public long CreatedAt { get; set; } + /// JSON Schema for the action input. + [JsonPropertyName("inputSchema")] + public JsonElement? InputSchema { get; set; } - /// Current phase identity, or null before any phase is entered. - [JsonPropertyName("currentPhase")] - public WorkflowCurrentPhase? CurrentPhase { get; set; } + /// Action name exposed by the canvas provider. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} - /// Resource ceilings declared by the workflow. - [JsonPropertyName("declaredLimits")] - public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } +/// Canvas available in the current session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class DiscoveredCanvas +{ + /// Actions the agent or host may invoke on an open instance. + [JsonPropertyName("actions")] + public IList? Actions { get; set; } - /// Number of phases declared by the workflow. - [JsonPropertyName("declaredPhaseCount")] - public long DeclaredPhaseCount { get; set; } + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; - /// Human-readable workflow description. + /// 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; - /// Number of direct workflow agents currently live. - [JsonPropertyName("liveAgentCount")] - public long LiveAgentCount { get; set; } + /// Human-readable canvas name. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Epoch milliseconds when this live-overlay snapshot was observed. - [JsonPropertyName("observedAt")] - public long ObservedAt { get; set; } + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; - /// Lifecycle and timing observations for each workflow phase. - [JsonPropertyName("phases")] - public IList Phases { get => field ??= []; set; } + /// Owning extension display name, when available. + [JsonPropertyName("extensionName")] + public string? ExtensionName { get; set; } - /// Bidirectional page of durable workflow progress. - [JsonPropertyName("progress")] - public WorkflowProgressPage Progress { get => field ??= new(); set; } + /// Host-local PNG path for the canvas icon, when supplied. + [JsonPropertyName("icon")] + public string? Icon { get; set; } - /// Monotonic durable run revision. - [JsonPropertyName("revision")] - public long Revision { get; set; } + /// JSON Schema for canvas open input. + [JsonPropertyName("inputSchema")] + public JsonElement? InputSchema { get; set; } +} - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; +/// 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; } +} - /// Epoch milliseconds when execution first started, or null before start. - [JsonPropertyName("startedAt")] - public long? StartedAt { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionCanvasListRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Current workflow run status. - [JsonPropertyName("status")] - public WorkflowRunStatus Status { 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; - /// Terminal run outcome, or null while nonterminal. - [JsonPropertyName("terminal")] - public WorkflowRunTerminal? Terminal { get; set; } + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; - /// Total direct workflow agents spawned across all attempts. - [JsonPropertyName("totalSpawnedAgentCount")] - public long TotalSpawnedAgentCount { get; set; } + /// Owning extension display name, when available. + [JsonPropertyName("extensionName")] + public string? ExtensionName { get; set; } - /// Epoch milliseconds when the durable run was last updated. - [JsonPropertyName("updatedAt")] - public long UpdatedAt { get; set; } + /// Host-local PNG path for the canvas icon, when supplied. + [JsonPropertyName("icon")] + public string? Icon { get; set; } - /// Registered workflow name. - [JsonPropertyName("workflowName")] - public string WorkflowName { get; set; } = string.Empty; -} + /// Input supplied when the instance was opened. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } -/// Parameters for paging workflow progress. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowGetRunProgressRequest -{ - /// Exclusive forward cursor. - [JsonPropertyName("afterSeq")] - public long? AfterSeq { get; set; } + /// Stable caller-supplied canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; - /// Exclusive backward cursor. - [JsonPropertyName("beforeSeq")] - public long? BeforeSeq { get; set; } + /// Provider-supplied status text. + [JsonPropertyName("status")] + public string? Status { get; set; } - /// Maximum records to return. Defaults to 200 and is capped at 500. - [JsonPropertyName("limit")] - public int? Limit { get; set; } + /// Rendered title. + [JsonPropertyName("title")] + public string? Title { get; set; } - /// Optional phase identifier used to scope records and cursors. - [JsonPropertyName("phaseId")] - public string? PhaseId { get; set; } + /// URL for web-rendered canvases. + [JsonPropertyName("url")] + public string? Url { get; set; } +} - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; +/// Live open-canvas snapshot. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CanvasListOpenResult +{ + /// Currently open canvas instances. + [JsonPropertyName("openCanvases")] + public IList OpenCanvases { get => field ??= []; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionCanvasListOpenRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Parameters for cancelling a workflow run. +/// Canvas open parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowCancelRequest +internal sealed class CanvasOpenRequest { - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; + + /// Owning provider identifier. Optional when the canvasId is unique across providers; required to disambiguate when multiple providers register the same canvasId. + [JsonPropertyName("extensionId")] + public string? ExtensionId { get; set; } + + /// Canvas open input. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } + + /// Caller-supplied stable instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Parameters for pausing a running workflow. +/// Canvas close parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowPauseRequest +internal sealed class CanvasCloseRequest { - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// 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 SessionWorkflowPauseAtCheckpoint operations. +/// Canvas action invocation result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkflowPauseAtCheckpointResult +public sealed class CanvasActionInvokeResult { - /// Whether this execution attempt must pause or may continue. - [JsonPropertyName("action")] - public WorkflowPauseCheckpointAction Action { get; set; } + /// Provider-supplied action result. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// Parameters for an owned durable pause checkpoint. +/// Canvas action invocation parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowPauseCheckpointRequest +internal sealed class CanvasActionInvokeRequest { - /// Opaque token identifying the execution attempt that reached the checkpoint. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + /// Action name to invoke. + [JsonPropertyName("actionName")] + public string ActionName { get; set; } = string.Empty; - /// Stable author-defined checkpoint key. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// Action input. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// 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; } -/// Acknowledgement that a workflow request was accepted. +/// Internal canvas provider registration parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAckResult +internal sealed class CanvasProviderRegisterRequest { -} + /// Canvas contributions supplied by the provider. + [JsonPropertyName("canvases")] + public IList Canvases { get => field ??= []; set; } -/// One ordered workflow progress line. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowLogLine -{ - /// Progress line kind. - [JsonPropertyName("kind")] - public WorkflowLogLineKind Kind { get; set; } + /// Connection identifier for callback routing. + [JsonPropertyName("connectionId")] + public string ConnectionId { get; set; } = string.Empty; - /// Monotonic sequence number within the workflow run. - [JsonPropertyName("seq")] - public long Seq { get; set; } + /// Provider metadata supplied by the host. + [JsonPropertyName("info")] + public JsonElement Info { get; set; } - /// Progress text. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Parameters for recording workflow progress. +/// Internal canvas provider unregistration parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowLogRequest +internal sealed class CanvasProviderUnregisterRequest { - /// 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; + /// 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; } -/// Result of one workflow-scoped subagent call. +/// Machine-readable workflow run failure. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAgentResult +[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 { - /// Agent result, omitted when the agent produced no result. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; } -/// Options for one workflow-scoped subagent call. + +/// The workflow_limit_reached variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAgentOptions +public partial class WorkflowRunFailureWorkflowLimitReached : WorkflowRunFailure { - /// 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; } + /// + [JsonIgnore] + public override string Type => "workflow_limit_reached"; - /// Optional label distinguishing otherwise identical memoized agent calls. - [JsonPropertyName("label")] - public string? Label { get; set; } + /// Resource ceiling that stopped the run. + [JsonPropertyName("kind")] + public required WorkflowRunFailureKind Kind { get; set; } - /// Optional model identifier for the subagent. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } - /// Optional reasoning effort override for the subagent. - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } + /// Suggested larger ceiling when the runtime can derive one safely. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("suggestedValue")] + public double? SuggestedValue { get; set; } - /// Optional JSON Schema for structured agent output. - [JsonPropertyName("schema")] - public JsonElement? Schema { get; set; } + /// Approved effective ceiling that was reached. + [JsonPropertyName("value")] + public required double Value { get; set; } } -/// Parameters for one workflow-scoped subagent call. +/// The workflow_resume_declined variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowAgentRequest +public partial class WorkflowRunFailureWorkflowResumeDeclined : WorkflowRunFailure { - /// 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; + /// + [JsonIgnore] + public override string Type => "workflow_resume_declined"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Human-readable reason the resume did not proceed. + [JsonPropertyName("reason")] + public required string Reason { get; set; } - /// Workflow run identifier that owns the subagent. - [JsonPropertyName("workflowRunId")] - public string WorkflowRunId { get; set; } = string.Empty; + /// Workflow run identifier whose changed limits were declined. + [JsonPropertyName("runId")] + public required string RunId { get; set; } } -/// Result of reading a workflow journal entry. +/// The workflow_durable_failure variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowJournalGetResult +public partial class WorkflowRunFailureWorkflowDurableFailure : WorkflowRunFailure { - /// 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; } -} + /// + [JsonIgnore] + public override string Type => "workflow_durable_failure"; -/// Parameters for reading a workflow journal entry. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowJournalGetRequest -{ - /// Opaque token identifying the current workflow execution attempt. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + /// Stable failure code. + [JsonPropertyName("code")] + public required string Code { get; set; } - /// Namespaced journal key. - [JsonPropertyName("key")] - public string Key { get; set; } = string.Empty; + /// Execution-critical durable operation that failed. + [JsonPropertyName("operation")] + public required WorkflowDurableOperation Operation { get; set; } /// Workflow run identifier. [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public required string RunId { get; set; } } -/// Parameters for storing a workflow journal entry. +/// The run stopped because its usage accounting could not be completed. +/// The workflow_accounting_incomplete variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkflowJournalPutRequest +public partial class WorkflowRunFailureWorkflowAccountingIncomplete : WorkflowRunFailure { - /// 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; + /// + [JsonIgnore] + public override string Type => "workflow_accounting_incomplete"; - /// JSON result to memoize. - [JsonPropertyName("resultJson")] - public JsonElement ResultJson { get; set; } + /// Confirmed usage in nano-AIU, representing the floor of what the run spent. + [JsonPropertyName("drainedNanoAiu")] + public required long DrainedNanoAiu { get; set; } /// Workflow run identifier. [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + public required string RunId { get; set; } } -/// The session's authoritative model snapshot. Auto preference fields are configuration for the virtual `auto` model and do not change the selected model identifier. The context tier reflects `Session.getContextTier()`, restored from the session journal on resume. +/// The extension that owns the workflow disconnected while the run was executing, so the host halted it. The run's journaled subagent results are preserved so a resume can reuse them. +/// The workflow_provider_disconnected variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CurrentModel +public partial class WorkflowRunFailureWorkflowProviderDisconnected : WorkflowRunFailure { - /// 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; } + /// + [JsonIgnore] + public override string Type => "workflow_provider_disconnected"; - /// 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; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public required string RunId { get; set; } } -/// Identifies the target session. +/// Durable metadata describing who initiated a workflow pause. +/// Polymorphic base type discriminated by type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModelGetCurrentRequest +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(WorkflowPauseInfoUser), "user")] +[JsonDerivedType(typeof(WorkflowPauseInfoCheckpoint), "checkpoint")] +public partial class WorkflowPauseInfo { - /// 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. + +/// The user variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSwitchConfirmation +public partial class WorkflowPauseInfoUser : WorkflowPauseInfo { - /// Current conversation token count before switching models. - [JsonPropertyName("currentTokens")] - public double CurrentTokens { get; set; } + /// + [JsonIgnore] + public override string Type => "user"; +} - /// Target model token limit used by the compaction preflight. - [JsonPropertyName("targetLimit")] - public double TargetLimit { get; set; } +/// The checkpoint variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class WorkflowPauseInfoCheckpoint : WorkflowPauseInfo +{ + /// + [JsonIgnore] + public override string Type => "checkpoint"; - /// Display name of the model that requires compaction confirmation. - [JsonPropertyName("targetModelDisplayName")] - public string TargetModelDisplayName { get; set; } = string.Empty; + /// Stable author-defined checkpoint key that initiated the pause. + [JsonPropertyName("key")] + public required string Key { get; set; } } -/// The model identifier active on the session after the switch. +/// Complete current or terminal workflow run envelope. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSwitchToResult +public sealed class WorkflowRunResult { - /// Compaction confirmation projection when status is confirmation_required. - [JsonPropertyName("confirmation")] - public ModelSwitchConfirmation? Confirmation { get; set; } + /// One-based execution attempt represented by this envelope. Absent before the first attempt starts or when returned by an older runtime. + [JsonPropertyName("attempt")] + public long? Attempt { get; set; } - /// 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; } + /// Error message for an errored run. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Deprecation warnings associated with the selected model or options. - [JsonPropertyName("deprecationWarnings")] - public IList? DeprecationWarnings { get; set; } + /// Machine-readable failure details for a halted or errored run. + [JsonPropertyName("failure")] + public WorkflowRunFailure? Failure { get; set; } - /// User-facing outcome message for the model switch. - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Structured pause initiator metadata for a paused attempt. + [JsonPropertyName("pauseInfo")] + public WorkflowPauseInfo? PauseInfo { get; set; } - /// Currently active model identifier after the switch. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// Reason for a halted or cancelled run. + [JsonPropertyName("reason")] + public string? Reason { 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; } + /// Completed workflow result. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } - /// Persistence failure encountered after applying the model switch. - [JsonPropertyName("persistenceError")] - public string? PersistenceError { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// Stable queue item identifier when this request was enqueued. Remains present if the item drains before the response is returned. - [JsonPropertyName("queueId")] - public string? QueueId { get; set; } + /// Partial journal and progress snapshot for a halted, cancelled, or errored run. + [JsonPropertyName("snapshot")] + public JsonElement? Snapshot { get; set; } - /// Lifecycle result for the requested switch. + /// Current or terminal workflow run status. [JsonPropertyName("status")] - public string? Status { get; set; } - - /// User-facing warning produced while applying the model switch. - [JsonPropertyName("warning")] - public string? Warning { get; set; } -} - -/// Vision-specific limits. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverrideLimitsVision -{ - /// Maximum image size in bytes. - [JsonPropertyName("max_prompt_image_size")] - public long? MaxPromptImageSize { get; set; } - - /// Maximum number of images per prompt. - [JsonPropertyName("max_prompt_images")] - public long? MaxPromptImages { get; set; } - - /// MIME types the model accepts. - [JsonPropertyName("supported_media_types")] - public IList? SupportedMediaTypes { get; set; } + public WorkflowRunStatus Status { get; set; } } -/// Token limits for prompts, outputs, and context window. +/// Wire-only per-invocation workflow resource ceiling overrides. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverrideLimits +public sealed class WorkflowRunLimits { - /// Maximum total context window size in tokens. - [JsonPropertyName("max_context_window_tokens")] - public long? MaxContextWindowTokens { get; set; } + /// Maximum AI credits consumed by workflow subagents and their descendants. The post-paid ceiling is soft: parallel turns can settle beyond it before the run stops. + [JsonPropertyName("maxAiCredits")] + public double? MaxAiCredits { get; set; } - /// Maximum number of output/completion tokens. - [JsonPropertyName("max_output_tokens")] - public long? MaxOutputTokens { get; set; } + /// Maximum number of workflow subagents that may run concurrently. + [JsonPropertyName("maxConcurrentSubagents")] + public long? MaxConcurrentSubagents { get; set; } - /// Maximum number of prompt/input tokens. - [JsonPropertyName("max_prompt_tokens")] - public long? MaxPromptTokens { get; set; } + /// Maximum total number of workflow subagents that may be admitted. + [JsonPropertyName("maxTotalSubagents")] + public long? MaxTotalSubagents { get; set; } - /// Vision-specific limits. - [JsonPropertyName("vision")] - public ModelCapabilitiesOverrideLimitsVision? Vision { 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; } } -/// Feature flags indicating what the model supports. +/// Options controlling workflow invocation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelCapabilitiesOverrideSupports +public sealed class WorkflowRunOptions { - /// 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; } + /// Per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { get; set; } - /// Whether this model supports reasoning effort configuration. - [JsonPropertyName("reasoningEffort")] - public bool? ReasoningEffort { get; set; } + /// Whether to emit workflow phase names to the session transcript. + [JsonPropertyName("logPhaseNames")] + public bool? LogPhaseNames { get; set; } - /// Whether this model supports canonical tool calling. - [JsonPropertyName("toolCalls")] - public bool? ToolCalls { get; set; } + /// Whether to notify the originating session when the workflow completes. + [JsonPropertyName("notifyOnComplete")] + public bool? NotifyOnComplete { get; set; } - /// Whether this model supports vision/image input. - [JsonPropertyName("vision")] - public bool? Vision { get; set; } + /// Run identifier whose journal and progress should seed this resumed run. + [JsonPropertyName("resumeFromRunId")] + public string? ResumeFromRunId { get; set; } } -/// Optional capability overrides (vision, tool_calls, reasoning, etc.). +/// Parameters for invoking a registered workflow. [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; } - - /// Feature flags indicating what the model supports. - [JsonPropertyName("supports")] - public ModelCapabilitiesOverrideSupports? Supports { get; set; } -} - -/// Environment variables consulted while resolving model-picker settings. -public sealed class ModelPickerSettingsContextEnvironment +internal sealed class WorkflowRunRequest { -} + /// Workflow input value. + [JsonPropertyName("args")] + public JsonElement Args { get; set; } -/// Filesystem and environment context used to resolve model-picker settings. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelPickerSettingsContext -{ - /// Optional Copilot configuration directory containing persisted settings. - [JsonPropertyName("configDir")] - public string? ConfigDir { get; set; } + /// Registered workflow name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Environment variables consulted while resolving model-picker settings. - [JsonPropertyName("environment")] - public ModelPickerSettingsContextEnvironment Environment { get => field ??= new(); set; } + /// Workflow invocation options. + [JsonPropertyName("options")] + public WorkflowRunOptions? Options { get; set; } - /// User home directory used when resolving persisted settings. - [JsonPropertyName("homeDirectory")] - public string HomeDirectory { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// RPC data type for ModelPickerPersistence operations. +/// Resolved persisted workflow identity and resumed run envelope. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelPickerPersistenceRequest +public sealed class WorkflowResumeResult { - /// Whether context tier was explicitly selected and should be persisted. - [JsonPropertyName("contextTierExplicit")] - public bool? ContextTierExplicit { get; set; } - - /// Whether reasoning effort was explicitly selected and should be persisted. - [JsonPropertyName("reasoningEffortExplicit")] - public bool? ReasoningEffortExplicit { get; set; } + /// Terminal resumed run envelope. + [JsonPropertyName("run")] + public WorkflowRunResult Run { get => field ??= new(); set; } - /// Filesystem and environment context used to resolve settings persistence. - [JsonPropertyName("settingsContext")] - public ModelPickerSettingsContext SettingsContext { get => field ??= new(); set; } + /// Persisted workflow name resolved for the resumed run. + [JsonPropertyName("workflowName")] + public string WorkflowName { get; set; } = string.Empty; } -/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. +/// Parameters for resuming a workflow run from its persisted identity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSwitchToRequest +internal sealed class WorkflowResumeRequest { - /// 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 selection id to switch to, as returned by `list`. A bare id (e.g. `claude-sonnet-4.6`) names a Copilot (CAPI) model; a provider-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a registry 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; } - - /// 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 per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { get; set; } - /// Optional repository settings scope to persist after the switch commits. - [JsonPropertyName("repoScope")] - public string? RepoScope { get; set; } + /// Whether to emit workflow phase names to the session transcript. + [JsonPropertyName("logPhaseNames")] + public bool? LogPhaseNames { get; set; } - /// Require the target to be currently available and enabled before applying the switch. - [JsonPropertyName("requireAvailable")] - public bool? RequireAvailable { get; set; } + /// Whether to notify the originating session when the workflow completes. + [JsonPropertyName("notifyOnComplete")] + public bool? NotifyOnComplete { get; set; } - /// When true, evaluate context-window compaction policy before applying the switch. - [JsonPropertyName("runCompactionPreflight")] - public bool? RunCompactionPreflight { 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; - - /// 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. +/// Options for an internal tool-originated workflow invocation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSwitchAutoTierResult +internal sealed class WorkflowToolRunOptions { - /// 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; } + /// Per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { 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; } + /// Run identifier whose journal and progress should seed this resumed run. + [JsonPropertyName("resumeFromRunId")] + public string? ResumeFromRunId { get; set; } } -/// An Auto preference request for the session. This updates Auto configuration only; it does not change the selected model to `auto`. +/// Internal parameters for invoking a registered workflow from a tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSwitchAutoTierRequest +internal sealed class WorkflowToolRunRequest { - /// 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; } + /// Workflow input value. + [JsonPropertyName("args")] + public JsonElement Args { get; set; } + + /// Registered workflow name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Tool-originated workflow invocation options. + [JsonPropertyName("options")] + public WorkflowToolRunOptions? Options { 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; } + /// Opaque identifier of the originating tool call. + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } } -/// Managed, repository, and CLI model overrides to overlay onto the session at startup. +/// Internal parameters for resuming a workflow run from a tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelApplyStartupOverlayRequest +internal sealed class WorkflowToolResumeRequest { - /// 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; } - - /// Reasoning effort selected by repository settings, when configured. - [JsonPropertyName("repoReasoningEffort")] - public string? RepoReasoningEffort { get; set; } + /// Optional per-invocation resource ceiling overrides. + [JsonPropertyName("limits")] + public WorkflowRunLimits? Limits { get; set; } - /// Model required by server-managed policy, when configured. - [JsonPropertyName("serverManagedModel")] - public string? ServerManagedModel { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} - -/// The applied host allowlist and effective session model policy after intersection. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSetAllowedModelsResult -{ - /// 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; } + /// Opaque identifier of the originating tool call. + [JsonPropertyName("toolCallId")] + public string? ToolCallId { 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. +/// Parameters for retrieving a workflow run. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSetAllowedModelsRequest +internal sealed class WorkflowGetRunRequest { - /// Exact model IDs to permit, or null to clear the host restriction. - [JsonPropertyName("allowedModels")] - public IList? AllowedModels { 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; } -/// 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. +/// Declared or approved workflow resource ceilings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelSetReasoningEffortResult +public sealed class WorkflowDeclaredLimits { - /// Reasoning effort level recorded on the session after the update. - [JsonPropertyName("reasoningEffort")] - public string ReasoningEffort { get; set; } = string.Empty; + /// 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 total subagents spawned by the run. + [JsonPropertyName("maxTotalSubagents")] + public long? MaxTotalSubagents { get; set; } + + /// Maximum accumulated active execution time in seconds. + [JsonPropertyName("timeoutSeconds")] + public double? TimeoutSeconds { get; set; } } -/// Reasoning effort level to apply to the currently selected model. +/// Durable workflow resource consumption. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModelSetReasoningEffortRequest +public sealed class WorkflowRunConsumed { - /// 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; + /// Accumulated active execution time in milliseconds. + [JsonPropertyName("activeMs")] + public long ActiveMs { get; set; } - /// 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; } } -/// Cost-category metadata for a CAPI model. +/// Current workflow phase identity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionModelPriceCategory +public sealed class WorkflowCurrentPhase { - /// CAPI model identifier. + /// Current phase identifier. [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; - /// Cost category assigned to the model. - [JsonPropertyName("priceCategory")] - public ModelPickerPriceCategory PriceCategory { get; set; } + /// Zero-based declared phase ordinal, or null for an undeclared phase. + [JsonPropertyName("ordinal")] + public long? Ordinal { 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. +/// Prompt-safe terminal workflow outcome. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModelProviderDescriptor +public sealed class WorkflowRunTerminal { - /// Opaque, stable provider id, stamped onto every model this provider returns. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Human-readable terminal error. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// The neutral provider kind. - [JsonPropertyName("kind")] - public ModelProviderKind Kind { get; set; } + /// Machine-readable terminal failure. + [JsonPropertyName("failure")] + public WorkflowRunFailure? Failure { 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; + /// Pause initiator metadata, or null when the run did not pause. + [JsonPropertyName("pauseInfo")] + public WorkflowPauseInfo? PauseInfo { get; set; } - /// Stable ordering key for presenting providers in a deterministic sequence. - [JsonPropertyName("ordering")] - public long Ordering { get; set; } + /// Human-readable terminal reason. + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// Prompt-safe preview of the completed result. + [JsonPropertyName("resultPreview")] + public string? ResultPreview { get; set; } } -/// The list of models available to this session. +/// Durable workflow run summary with read-time live overlays. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionModelList +public sealed class WorkflowRunSummary { - /// 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; } + /// Epoch milliseconds when the current active segment started, or null while inactive. + [JsonPropertyName("activeSegmentStartedAt")] + public long? ActiveSegmentStartedAt { 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; } + /// Approved effective resource ceilings, or null until approved. + [JsonPropertyName("approved")] + public WorkflowDeclaredLimits? Approved { 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; } + /// Whether the durable run state currently passes runtime resume eligibility checks. + [JsonPropertyName("canResume")] + public bool CanResume { get; set; } - /// Per-quota snapshots returned alongside the model list, keyed by quota type. - [JsonPropertyName("quotaSnapshots")] - public IDictionary? QuotaSnapshots { get; set; } -} + /// Epoch milliseconds when the run completed, or null while nonterminal. + [JsonPropertyName("completedAt")] + public long? CompletedAt { 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; } -} + /// Durable resource consumption. + [JsonPropertyName("consumed")] + public WorkflowRunConsumed Consumed { get => field ??= new(); set; } -/// RPC data type for SessionModelListRequestWithSession operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModelListRequestWithSession -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Epoch milliseconds when the run was created. + [JsonPropertyName("createdAt")] + public long CreatedAt { get; set; } - /// If true, bypasses the per-session model list cache and re-fetches from CAPI. - [JsonPropertyName("skipCache")] - public bool? SkipCache { get; set; } -} + /// Current phase identity, or null before any phase is entered. + [JsonPropertyName("currentPhase")] + public WorkflowCurrentPhase? CurrentPhase { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionModeGetRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Resource ceilings declared by the workflow. + [JsonPropertyName("declaredLimits")] + public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } -/// Outcome of a session mode change, including any model switch it triggered and follow-up the host must perform. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ModeSetResult -{ - /// Whether the host should arm an interactive continuation after the mode change. - [JsonPropertyName("armInteractiveContinuation")] - public bool? ArmInteractiveContinuation { get; set; } + /// Number of phases declared by the workflow. + [JsonPropertyName("declaredPhaseCount")] + public long DeclaredPhaseCount { get; set; } - /// Compaction confirmation required before the mode change can complete. - [JsonPropertyName("confirmation")] - public ModelSwitchConfirmation? Confirmation { get; set; } + /// Human-readable workflow description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Whether the host must defer implementing the requested mode change. - [JsonPropertyName("deferImplementation")] - public bool? DeferImplementation { get; set; } + /// Number of direct workflow agents currently live. + [JsonPropertyName("liveAgentCount")] + public long LiveAgentCount { get; set; } - /// Deprecation warnings associated with the model selected by the mode change. - [JsonPropertyName("deprecationWarnings")] - public IList? DeprecationWarnings { get; set; } + /// Epoch milliseconds when this live-overlay snapshot was observed. + [JsonPropertyName("observedAt")] + public long ObservedAt { get; set; } - /// User-facing outcome message for the model switch triggered by the mode change. - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Monotonic durable run revision. + [JsonPropertyName("revision")] + public long Revision { 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; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// Whether applying the mode changed the active model. - [JsonPropertyName("modelChanged")] - public bool ModelChanged { get; set; } + /// Epoch milliseconds when execution first started, or null before start. + [JsonPropertyName("startedAt")] + public long? StartedAt { get; set; } - /// Lifecycle status of the requested mode change. + /// Current workflow run status. [JsonPropertyName("status")] - public string Status { get; set; } = string.Empty; - - /// User-facing warning produced while applying the mode change. - [JsonPropertyName("warning")] - public string? Warning { get; set; } -} + public WorkflowRunStatus Status { get; set; } -/// Agent interaction mode to apply to the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ModeSetRequest -{ - /// Explicit response to a model-switch compaction preflight. - [JsonPropertyName("compactionDecision")] - public string? CompactionDecision { get; set; } + /// Terminal run outcome, or null while nonterminal. + [JsonPropertyName("terminal")] + public WorkflowRunTerminal? Terminal { 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; } + /// Total direct workflow agents spawned across all attempts. + [JsonPropertyName("totalSpawnedAgentCount")] + public long TotalSpawnedAgentCount { get; set; } - /// Session whose plan-mode base state should be inherited. - [JsonPropertyName("inheritPlanBaseFromSessionId")] - public string? InheritPlanBaseFromSessionId { get; set; } + /// Epoch milliseconds when the durable run was last updated. + [JsonPropertyName("updatedAt")] + public long UpdatedAt { get; set; } - /// The session mode the agent is operating in. - [JsonPropertyName("mode")] - public SessionMode Mode { get; set; } + /// Registered workflow name. + [JsonPropertyName("workflowName")] + public string WorkflowName { get; set; } = string.Empty; +} - /// Whether the selected plan model should be persisted. - [JsonPropertyName("persistPlanSelection")] - public bool? PersistPlanSelection { get; set; } +/// 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; } - /// Settings context used when persisting the selected plan model. - [JsonPropertyName("pickerSettingsContext")] - public ModelPickerSettingsContext? PickerSettingsContext { get; set; } + /// Newest terminal-run cursor in this page, or null when the terminal window is empty. + [JsonPropertyName("newestSeq")] + public long? NewestSeq { get; set; } - /// Context tier to use with the dedicated plan model. - [JsonPropertyName("planContextTier")] - public string? PlanContextTier { get; set; } + /// Oldest terminal-run cursor in this page, or null when the terminal window is empty. + [JsonPropertyName("oldestSeq")] + public long? OldestSeq { get; set; } - /// Action to perform when leaving plan mode. - [JsonPropertyName("planExitAction")] - public string? PlanExitAction { get; set; } + /// Number of terminal runs older than this page. + [JsonPropertyName("omittedOlder")] + public long? OmittedOlder { get; set; } - /// Dedicated model to use in plan mode, when configured. - [JsonPropertyName("planModel")] - public string? PlanModel { get; set; } + /// Workflow run summaries in durable creation order. + [JsonPropertyName("runs")] + public IList Runs { get => field ??= []; set; } +} - /// Whether a dedicated plan model is configured. - [JsonPropertyName("planModelConfigured")] - public bool? PlanModelConfigured { get; set; } +/// Parameters for paging workflow runs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class WorkflowListRunsRequest +{ + /// Exclusive forward cursor. + [JsonPropertyName("afterSeq")] + public long? AfterSeq { get; set; } - /// Reasoning effort to use with the dedicated plan model. - [JsonPropertyName("planReasoningEffort")] - public string? PlanReasoningEffort { get; set; } + /// Exclusive backward cursor. + [JsonPropertyName("beforeSeq")] + public long? BeforeSeq { get; set; } - /// Whether leaving plan mode should restore the session's previous model. - [JsonPropertyName("restorePlanModel")] - public bool? RestorePlanModel { get; set; } + /// Maximum terminal runs to return. Defaults to 200 and is capped at 500. + [JsonPropertyName("limit")] + public int? Limit { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The session's friendly name, or null when not yet set. +/// Prompt-safe durable identity and live status for a direct workflow agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class NameGetResult +public sealed class WorkflowAgentSummary { - /// The session name (user-set or auto-generated), or null if not yet set. - [JsonPropertyName("name")] - public string? Name { get; set; } -} + /// Accumulated active agent time in milliseconds. + [JsonPropertyName("activeMs")] + public long ActiveMs { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionNameGetRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Prompt-safe live activity text. + [JsonPropertyName("activity")] + public string? Activity { get; set; } -/// New friendly name to apply to the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class NameSetRequest -{ - /// 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; + /// Stable direct-agent identifier. + [JsonPropertyName("agentId")] + public string AgentId { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Registered agent type. + [JsonPropertyName("agentType")] + public string AgentType { get; set; } = string.Empty; -/// Indicates whether the auto-generated summary was applied as the session's name. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class NameSetAutoResult -{ - /// Whether the auto-generated summary was persisted. False if the session already has a user-set name, the summary normalized to empty, or the session does not have a workspace. - [JsonPropertyName("applied")] - public bool Applied { get; set; } -} + /// Epoch milliseconds when the agent completed. + [JsonPropertyName("completedAt")] + public long? CompletedAt { get; set; } -/// Auto-generated session summary to apply as the session's name when no user-set name exists. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class NameSetAutoRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Friendly, non-unique name intended for display. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } - /// Auto-generated session summary. Empty/whitespace-only values are ignored; values are trimmed before persisting. - [JsonPropertyName("summary")] - public string Summary { get; set; } = string.Empty; -} + /// Friendly, non-unique name intended for display. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; -/// Existence, contents, and resolved path of the session plan file. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanReadResult -{ - /// The content of the plan file, or null if it does not exist. - [JsonPropertyName("content")] - public string? Content { get; set; } + /// Phase identifier active when the agent was launched, or null. + [JsonPropertyName("phaseId")] + public string? PhaseId { get; set; } - /// Whether the plan file exists in the workspace. - [JsonPropertyName("exists")] - public bool Exists { get; set; } + /// Model requested when the agent was launched. + [JsonPropertyName("requestedModel")] + public string? RequestedModel { get; set; } - /// Absolute file path of the plan file, or null if workspace is not enabled. - [JsonPropertyName("path")] - public string? Path { get; set; } -} + /// Concrete model resolved for the agent. + [JsonPropertyName("resolvedModel")] + public string? ResolvedModel { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanReadRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Owning workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; -/// Replacement contents to write to the session plan file. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PlanUpdateRequest -{ - /// The new content for the plan file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Epoch milliseconds when the agent started. + [JsonPropertyName("startedAt")] + public long? StartedAt { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Current durable or live agent status. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanDeleteRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Tool-call identifier that launched the agent. + [JsonPropertyName("toolCallId")] + public string ToolCallId { 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. +/// Durable lifecycle and timing for one workflow phase. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanSqlTodosRow +public sealed class WorkflowPhaseObservation { - /// 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; } + /// Completed active time accumulated by this phase in milliseconds. + [JsonPropertyName("accumulatedActiveMs")] + public long AccumulatedActiveMs { get; set; } - /// Todo description. - [JsonPropertyName("description")] - public string? Description { 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; } - /// Todo identifier. + /// 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; } + + /// Phase identifier. [JsonPropertyName("id")] - public string? Id { get; set; } + public string Id { get; set; } = string.Empty; - /// Todo status. + /// Most recent run attempt that entered this phase, or `0` if the phase has never been entered. + [JsonPropertyName("lastEnteredRunAttempt")] + public long LastEnteredRunAttempt { get; set; } + + /// Direct agents in this phase that are currently live. + [JsonPropertyName("liveAgentCount")] + public long LiveAgentCount { get; set; } + + /// 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 long? StartedAt { get; set; } + + /// Derived lifecycle state of the phase. [JsonPropertyName("status")] - public string? Status { get; set; } + public WorkflowPhaseStatus Status { get; set; } - /// Todo title. + /// Human-readable phase title. [JsonPropertyName("title")] - public string? Title { get; set; } -} + public string Title { get; set; } = string.Empty; -/// 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; } + /// Total direct agents associated with this phase. + [JsonPropertyName("totalAgentCount")] + public long TotalAgentCount { get; set; } } -/// Identifies the target session. +/// One durable workflow progress record. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPlanReadSqlTodosRequest +public sealed class WorkflowProgressLine { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Resume attempt that emitted this record. + [JsonPropertyName("attempt")] + public long Attempt { get; set; } -/// 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 PlanSqlTodoDependency -{ - /// ID of the todo it depends on. - [JsonPropertyName("dependsOn")] - public string DependsOn { get; set; } = string.Empty; + /// Progress record kind. + [JsonPropertyName("kind")] + public WorkflowLogLineKind Kind { get; set; } - /// ID of the todo that has the dependency. - [JsonPropertyName("todoId")] - public string TodoId { get; set; } = string.Empty; -} + /// Phase active when the record was emitted, or null before any phase. + [JsonPropertyName("phaseId")] + public string? PhaseId { get; set; } -/// Todo rows + dependency edges read from the session SQL database. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PlanReadSqlTodosWithDependenciesResult -{ - /// 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; } + /// Epoch milliseconds when the record was persisted. + [JsonPropertyName("recordedAt")] + public long RecordedAt { get; set; } - /// Rows from the session SQL todos table, ordered by creation time with insertion order used to break ties when available and id used for WITHOUT ROWID tables. Empty when no database, no todos table, or the SELECT failed. - [JsonPropertyName("rows")] - public IList Rows { get => field ??= []; set; } + /// 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 SessionPlanReadSqlTodosWithDependenciesRequest +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; } -/// RPC data type for WorkspacesGetWorkspaceResultWorkspace operations. -public sealed class WorkspacesGetWorkspaceResultWorkspace -{ - /// Current Git branch. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// Whether progress records older than this page exist. + [JsonPropertyName("hasMoreOlder")] + public bool HasMoreOlder { get; set; } - /// Whether the per-session Chronicle upgrade prompt was dismissed for the workspace. - [JsonPropertyName("chronicle_sync_dismissed")] - public bool? ChronicleSyncDismissed { get; set; } + /// Newest sequence number in this page, or null when empty. + [JsonPropertyName("newestSeq")] + public long? NewestSeq { get; set; } - /// Name of the client that created the workspace. - [JsonPropertyName("client_name")] - public string? ClientName { get; set; } + /// Oldest sequence number in this page, or null when empty. + [JsonPropertyName("oldestSeq")] + public long? OldestSeq { get; set; } - /// Timestamp when the workspace was created. - [JsonPropertyName("created_at")] - public DateTimeOffset? CreatedAt { get; set; } + /// Progress records in sequence order. + [JsonPropertyName("records")] + public IList Records { get => field ??= []; set; } - /// Current working directory associated with the workspace. - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// Run revision reflected by this page. + [JsonPropertyName("revision")] + public long Revision { get; set; } +} - /// Git repository root associated with the workspace. - [JsonPropertyName("git_root")] - public string? GitRoot { get; set; } +/// Full workflow run observability detail. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkflowRunDetail +{ + /// Epoch milliseconds when the current active segment started, or null while inactive. + [JsonPropertyName("activeSegmentStartedAt")] + public long? ActiveSegmentStartedAt { get; set; } - /// Allowed values for the `WorkspacesWorkspaceDetailsHostType` enumeration. - [JsonPropertyName("host_type")] - public WorkspacesWorkspaceDetailsHostType? HostType { get; set; } + /// Durable identities and live statuses for direct workflow agents. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; 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; + /// Approved effective resource ceilings, or null until approved. + [JsonPropertyName("approved")] + public WorkflowDeclaredLimits? Approved { get; set; } - /// Most recent Mission Control event identifier observed for the workspace. - [JsonPropertyName("mc_last_event_id")] - public string? McLastEventId { get; set; } + /// Whether the durable run state currently passes runtime resume eligibility checks. + [JsonPropertyName("canResume")] + public bool CanResume { get; set; } - /// Mission Control session identifier associated with the workspace. - [JsonPropertyName("mc_session_id")] - public string? McSessionId { get; set; } + /// Epoch milliseconds when the run completed, or null while nonterminal. + [JsonPropertyName("completedAt")] + public long? CompletedAt { get; set; } - /// Mission Control task identifier associated with the workspace. - [JsonPropertyName("mc_task_id")] - public string? McTaskId { get; set; } + /// Durable resource consumption. + [JsonPropertyName("consumed")] + public WorkflowRunConsumed Consumed { get => field ??= new(); set; } - /// Workspace display name. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Epoch milliseconds when the run was created. + [JsonPropertyName("createdAt")] + public long CreatedAt { get; set; } - /// Whether the workspace session can be steered remotely. - [JsonPropertyName("remote_steerable")] - public bool? RemoteSteerable { get; set; } + /// Current phase identity, or null before any phase is entered. + [JsonPropertyName("currentPhase")] + public WorkflowCurrentPhase? CurrentPhase { get; set; } - /// Repository identifier associated with the workspace. - [JsonPropertyName("repository")] - public string? Repository { get; set; } + /// Resource ceilings declared by the workflow. + [JsonPropertyName("declaredLimits")] + public WorkflowDeclaredLimits DeclaredLimits { get => field ??= new(); set; } - /// Number of persisted summaries in the workspace. - [JsonPropertyName("summary_count")] - public long? SummaryCount { get; set; } + /// Number of phases declared by the workflow. + [JsonPropertyName("declaredPhaseCount")] + public long DeclaredPhaseCount { get; set; } - /// Timestamp when the workspace was last updated. - [JsonPropertyName("updated_at")] - public DateTimeOffset? UpdatedAt { get; set; } + /// Human-readable workflow description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Whether the workspace name was explicitly chosen by the user. - [JsonPropertyName("user_named")] - public bool? UserNamed { get; set; } -} + /// Number of direct workflow agents currently live. + [JsonPropertyName("liveAgentCount")] + public long LiveAgentCount { get; set; } -/// Current workspace metadata for the session, including its absolute filesystem path when available. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesGetWorkspaceResult -{ - /// Absolute filesystem path to the workspace directory. Omitted when the session has no workspace (e.g. remote sessions). - [JsonPropertyName("path")] - public string? Path { get; set; } + /// Epoch milliseconds when this live-overlay snapshot was observed. + [JsonPropertyName("observedAt")] + public long ObservedAt { get; set; } - /// Current workspace metadata, or null if not available. - [JsonPropertyName("workspace")] - public WorkspacesGetWorkspaceResultWorkspace? Workspace { get; set; } + /// Lifecycle and timing observations for each workflow phase. + [JsonPropertyName("phases")] + public IList Phases { get => field ??= []; set; } + + /// Bidirectional page of durable workflow progress. + [JsonPropertyName("progress")] + public WorkflowProgressPage Progress { get => field ??= new(); set; } + + /// Monotonic durable run revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; + + /// Epoch milliseconds when execution first started, or null before start. + [JsonPropertyName("startedAt")] + public long? StartedAt { get; set; } + + /// Current workflow run status. + [JsonPropertyName("status")] + public WorkflowRunStatus Status { get; set; } + + /// Terminal run outcome, or null while nonterminal. + [JsonPropertyName("terminal")] + public WorkflowRunTerminal? Terminal { get; set; } + + /// Total direct workflow agents spawned across all attempts. + [JsonPropertyName("totalSpawnedAgentCount")] + public long TotalSpawnedAgentCount { get; set; } + + /// Epoch milliseconds when the durable run was last updated. + [JsonPropertyName("updatedAt")] + public long UpdatedAt { get; set; } + + /// Registered workflow name. + [JsonPropertyName("workflowName")] + public string WorkflowName { get; set; } = string.Empty; } -/// Identifies the target session. +/// Parameters for paging workflow progress. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesGetWorkspaceRequest +internal sealed class WorkflowGetRunProgressRequest { + /// Exclusive forward cursor. + [JsonPropertyName("afterSeq")] + public long? AfterSeq { get; set; } + + /// Exclusive backward cursor. + [JsonPropertyName("beforeSeq")] + public long? BeforeSeq { get; set; } + + /// Maximum records to return. Defaults to 200 and is capped at 500. + [JsonPropertyName("limit")] + public int? Limit { get; set; } + + /// Optional phase identifier used to scope records and cursors. + [JsonPropertyName("phaseId")] + public string? PhaseId { get; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Workspace metadata fields to update. +/// Parameters for cancelling a workflow run. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesUpdateMetadataRequest +internal sealed class WorkflowCancelRequest { - /// 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; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Optional session context used when creating a local workspace. +/// Parameters for pausing a running workflow. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesEnsureRequest +internal sealed class WorkflowPauseRequest { - /// Opaque workspace context supplied by the session host. - [JsonPropertyName("context")] - public JsonElement? Context { 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; } -/// Relative paths of files stored in the session workspace files directory. +/// RPC data type for SessionWorkflowPauseAtCheckpoint operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesListFilesResult +internal sealed class SessionWorkflowPauseAtCheckpointResult { - /// Slash-separated relative file paths in the workspace files directory. - [JsonPropertyName("files")] - public IList Files { get => field ??= []; set; } + /// Whether this execution attempt must pause or may continue. + [JsonPropertyName("action")] + public WorkflowPauseCheckpointAction Action { get; set; } } -/// Identifies the target session. +/// Parameters for an owned durable pause checkpoint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesListFilesRequest +internal sealed class WorkflowPauseCheckpointRequest { + /// 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; } -/// Contents of the requested workspace file as a UTF-8 string. +/// Acknowledgement that a workflow request was accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesReadFileResult +public sealed class WorkflowAckResult { - /// File content as a UTF-8 string. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; } -/// Relative path of the workspace file to read. +/// One ordered workflow progress line. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesReadFileRequest +public sealed class WorkflowLogLine { - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Progress line kind. + [JsonPropertyName("kind")] + public WorkflowLogLineKind Kind { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } -/// Relative path and UTF-8 content for the workspace file to create or overwrite. +/// Parameters for recording workflow progress. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesCreateFileRequest +internal sealed class WorkflowLogRequest { - /// File content to write as a UTF-8 string. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Opaque token identifying the current workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { 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; } -/// Filesystem metadata for a path in the session workspace files directory. +/// Result of one workflow-scoped subagent call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesStatFileResult +public sealed class WorkflowAgentResult { - /// Creation time in Unix epoch milliseconds. - [JsonPropertyName("birthtimeMs")] - public double BirthtimeMs { get; set; } + /// Agent result, omitted when the agent produced no result. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } +} - /// Whether the path identifies a directory. - [JsonPropertyName("isDirectory")] - public bool IsDirectory { get; set; } +/// Options for one workflow-scoped subagent call. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkflowAgentOptions +{ + /// Optional built-in or custom agent name whose definition configures the subagent. + [JsonPropertyName("agent")] + public string? Agent { get; set; } - /// Whether the path identifies a regular file. - [JsonPropertyName("isFile")] - public bool IsFile { get; set; } + /// Optional context tier override for the subagent. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } - /// Last modification time in Unix epoch milliseconds. - [JsonPropertyName("mtimeMs")] - public double MtimeMs { get; set; } + /// Optional label distinguishing otherwise identical memoized agent calls. + [JsonPropertyName("label")] + public string? Label { get; set; } - /// Size in bytes. - [JsonPropertyName("size")] - public double Size { 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; } } -/// Relative path of the workspace file or directory to inspect. +/// Parameters for one workflow-scoped subagent call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesStatFileRequest +internal sealed class WorkflowAgentRequest { - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// 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; + + /// Workflow run identifier that owns the subagent. + [JsonPropertyName("workflowRunId")] + public string WorkflowRunId { get; set; } = string.Empty; } -/// Directory to create within the session workspace files directory. +/// Result of reading a workflow journal entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesCreateDirectoryRequest +public sealed class WorkflowJournalGetResult { - /// 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; } + /// Whether the journal contained the requested key. + [JsonPropertyName("hit")] + public bool Hit { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Cached JSON result. The hit field distinguishes a cached JSON null from a miss. + [JsonPropertyName("resultJson")] + public JsonElement? ResultJson { get; set; } } -/// File or directory to remove from the session workspace files directory. +/// Parameters for reading a workflow journal entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesRemovePathRequest +internal sealed class WorkflowJournalGetRequest { - /// Whether a missing path should be treated as success. Defaults to false. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// Opaque token identifying the current workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Slash-separated relative path within the workspace files directory. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Namespaced journal key. + [JsonPropertyName("key")] + public string Key { get; set; } = string.Empty; - /// Whether to remove directory contents recursively. Defaults to false. - [JsonPropertyName("recursive")] - public bool? Recursive { 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; } -/// Source and destination paths for a rename within the session workspace files directory. +/// Parameters for storing a workflow journal entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesRenamePathRequest +internal sealed class WorkflowJournalPutRequest { - /// Slash-separated destination path relative to the workspace files directory. - [JsonPropertyName("destination")] - public string Destination { get; set; } = string.Empty; + /// 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; + + /// JSON result to memoize. + [JsonPropertyName("resultJson")] + public JsonElement ResultJson { get; set; } + + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Slash-separated source path relative to the workspace files directory. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; } -/// Workspace checkpoint metadata with assigned number, human-readable title, and checkpoint filename. +/// The session's authoritative model snapshot. Auto preference fields are configuration for the virtual `auto` model and do not change the selected model identifier. The context tier reflects `Session.getContextTier()`, restored from the session journal on resume. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesCheckpoints +public sealed class CurrentModel { - /// Filename of the checkpoint within the workspace checkpoints directory. - [JsonPropertyName("filename")] - public string Filename { 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; } - /// Checkpoint number assigned by the workspace manager. - [JsonPropertyName("number")] - public long Number { 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; } - /// Human-readable checkpoint title. - [JsonPropertyName("title")] - public string Title { get; set; } = string.Empty; -} + /// Context tier for models that support multiple context-window sizes. + [JsonPropertyName("contextTier")] + public ContextTier? ContextTier { get; set; } -/// 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; } + /// 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; } + + /// 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; } + + /// 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 SessionWorkspacesListCheckpointsRequest +internal sealed class SessionModelGetCurrentRequest { /// 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. +/// RPC data type for ModelSwitchConfirmation operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesReadCheckpointResult +public sealed class ModelSwitchConfirmation { - /// Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing. - [JsonPropertyName("content")] - public string? Content { get; set; } + /// Current conversation token count before switching models. + [JsonPropertyName("currentTokens")] + public double CurrentTokens { get; set; } + + /// Target model token limit used by the compaction preflight. + [JsonPropertyName("targetLimit")] + public double TargetLimit { get; set; } + + /// Display name of the model that requires compaction confirmation. + [JsonPropertyName("targetModelDisplayName")] + public string TargetModelDisplayName { get; set; } = string.Empty; } -/// Checkpoint number to read. +/// The model identifier active on the session after the switch. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesReadCheckpointRequest +public sealed class ModelSwitchToResult { - /// Checkpoint number to read. - [JsonPropertyName("number")] - public long Number { get; set; } + /// Compaction confirmation projection when status is confirmation_required. + [JsonPropertyName("confirmation")] + public ModelSwitchConfirmation? Confirmation { get; set; } - /// 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; } -/// Metadata for the persisted summary. -public sealed class WorkspacesAddSummaryResultSummary -{ + /// 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; } + + /// 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; } } -/// Refreshed metadata for the containing workspace. -public sealed class WorkspacesAddSummaryResultWorkspace +/// Environment variables consulted while resolving model-picker settings. +public sealed class ModelPickerSettingsContextEnvironment { } -/// Persisted summary metadata and refreshed workspace metadata. +/// Filesystem and environment context used to resolve model-picker settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesAddSummaryResult +public sealed class ModelPickerSettingsContext { - /// Metadata for the persisted summary. - [JsonPropertyName("summary")] - public WorkspacesAddSummaryResultSummary? Summary { get; set; } + /// Optional Copilot configuration directory containing persisted settings. + [JsonPropertyName("configDir")] + public string? ConfigDir { get; set; } - /// Refreshed metadata for the containing workspace. - [JsonPropertyName("workspace")] - public WorkspacesAddSummaryResultWorkspace? Workspace { get; set; } + /// Environment variables consulted while resolving model-picker settings. + [JsonPropertyName("environment")] + public ModelPickerSettingsContextEnvironment Environment { get => field ??= new(); set; } + + /// User home directory used when resolving persisted settings. + [JsonPropertyName("homeDirectory")] + public string HomeDirectory { get; set; } = string.Empty; } -/// Compaction summary checkpoint to persist. +/// RPC data type for ModelPickerPersistence operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesAddSummaryRequest +public sealed class ModelPickerPersistenceRequest { - /// Markdown summary content to persist. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Whether context tier was explicitly selected and should be persisted. + [JsonPropertyName("contextTierExplicit")] + public bool? ContextTierExplicit { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether reasoning effort was explicitly selected and should be persisted. + [JsonPropertyName("reasoningEffortExplicit")] + public bool? ReasoningEffortExplicit { get; set; } - /// Summary title shown in checkpoint listings. - [JsonPropertyName("title")] - public string Title { get; set; } = string.Empty; + /// Filesystem and environment context used to resolve settings persistence. + [JsonPropertyName("settingsContext")] + public ModelPickerSettingsContext SettingsContext { get => field ??= new(); set; } } -/// Rollback point for local workspace summaries. +/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesTruncateSummariesRequest +public sealed class ModelSwitchToRequest { - /// Number of newest summaries to keep. - [JsonPropertyName("keepCount")] - public long KeepCount { 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; } + + /// 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; } + + /// 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 ModelChangeSource? Source { get; set; } + + /// Output verbosity level to request for supported models. + [JsonPropertyName("verbosity")] + public Verbosity? Verbosity { get; set; } +} + +/// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ModelSwitchToRequestWithSession +{ + /// 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; } /// 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; } } -/// Autopilot objective file content, or null when missing. +/// 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 WorkspacesReadAutopilotObjectiveResult +public sealed class ModelSwitchAutoTierResult { - /// Autopilot objective file content, or null when missing. - [JsonPropertyName("content")] - public string? Content { get; set; } + /// Auto preference currently claimed by an in-progress activation. Null means the activation is returning to provider-default routing. + [JsonPropertyName("activatingAutoTier")] + public AutoTier? ActivatingAutoTier { get; set; } + + /// Auto preference currently committed for the session. + [JsonPropertyName("effectiveAutoTier")] + public AutoTier? EffectiveAutoTier { get; set; } + + /// Latest unclaimed Auto preference waiting for a future user turn. + [JsonPropertyName("pendingAutoTier")] + public AutoTier? PendingAutoTier { get; set; } + + /// Immediate request status. `pending` means accepted but not committed. + [JsonPropertyName("status")] + public ModelSwitchAutoTierStatus Status { get; set; } + + /// Earlier unclaimed preference replaced by this request. This can be present with either status, including when selecting the effective preference cancels pending work. + [JsonPropertyName("supersededAutoTier")] + public AutoTier? SupersededAutoTier { get; set; } } -/// Identifies the target session. +/// An Auto preference request for the session. This updates Auto configuration only; it does not change the selected model to `auto`. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesReadAutopilotObjectiveRequest +internal sealed class ModelSwitchAutoTierRequest { + /// Auto preference to activate when a future user turn using the `auto` model safely mints a replacement model and token pair. Pass null to return to provider-default Auto routing. + [JsonPropertyName("autoTier")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + public AutoTier? AutoTier { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} -/// Result of writing the autopilot objective file. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesWriteAutopilotObjectiveResult -{ - /// Filesystem operation performed. - [JsonPropertyName("operation")] - public string Operation { 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; } } -/// Autopilot objective file content to persist. +/// Managed, repository, and CLI model overrides to overlay onto the session at startup. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesWriteAutopilotObjectiveRequest +internal sealed class ModelApplyStartupOverlayRequest { - /// Autopilot objective file content. - [JsonPropertyName("content")] - public string Content { 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; } -/// Result of deleting the autopilot objective file. +/// RPC data type for SessionModelClearStartupSeed operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesDeleteAutopilotObjectiveResult +internal sealed class SessionModelClearStartupSeedResult { - /// True when a file was deleted. - [JsonPropertyName("deleted")] - public bool Deleted { get; set; } + /// Gets or sets the cleared value. + [JsonPropertyName("cleared")] + public bool Cleared { get; set; } } -/// Identifies the target session. +/// RPC data type for SessionModelClearStartupSeed operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesDeleteAutopilotObjectiveRequest +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; } -/// Whether the autopilot objective file exists. +/// The applied host allowlist and effective session model policy after intersection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesAutopilotObjectiveExistsResult +public sealed class ModelSetAllowedModelsResult { - /// True when the objective file exists. - [JsonPropertyName("exists")] - public bool Exists { get; set; } + /// Normalized host allowlist. Omitted when the host restriction was cleared, or when a relay client does not return the host policy. + [JsonPropertyName("allowedModels")] + public IList? AllowedModels { get; set; } + + /// Effective exact IDs or repository policy patterns after applying the host restriction. Omitted by relay clients that do not return the host policy. + [JsonPropertyName("effectiveAllowedModels")] + public IList? EffectiveAllowedModels { get; set; } + + /// Effective deterministic fallback model, when the policy defines one. + [JsonPropertyName("fallbackModel")] + public string? FallbackModel { get; set; } + + /// Selected session model after reconciling a now-disallowed concrete selection. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } } -/// Identifies the target session. +/// Host-supplied exact model selection IDs to allow for this running session. CAPI IDs are intersected with repository `.github/allowed_models.txt` policy; provider-qualified IDs remain exempt from repository-only policy but are restricted by this host list. Omit or pass null to clear the host restriction; an explicit empty or disjoint list is rejected. Validation and pre-selection fallback failures preserve the previous restriction. Failures after a fallback selection commits retain the new restriction and selected model; callers should inspect current session state after such an error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionWorkspacesAutopilotObjectiveExistsRequest +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; } -/// RPC data type for WorkspacesSaveLargePasteResultSaved operations. -public sealed class WorkspacesSaveLargePasteResultSaved -{ - /// Filename within the workspace files directory. - [JsonPropertyName("filename")] - public string Filename { get; set; } = string.Empty; - - /// Absolute filesystem path to the saved paste file. - [JsonPropertyName("filePath")] - public string FilePath { get; set; } = string.Empty; - - /// Size of the saved file in bytes. - [JsonPropertyName("sizeBytes")] - public long SizeBytes { get; set; } -} - -/// Descriptor for the saved paste file, or null when the workspace is unavailable. +/// Update the session's reasoning effort without changing the selected model. Use `switchTo` instead when you also need to change the model. The runtime stores the effort on the session and applies it to subsequent turns. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspacesSaveLargePasteResult +public sealed class ModelSetReasoningEffortResult { - /// 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; } + /// Reasoning effort level recorded on the session after the update. + [JsonPropertyName("reasoningEffort")] + public string ReasoningEffort { get; set; } = string.Empty; } -/// Pasted content to save as a UTF-8 file in the session workspace. +/// Reasoning effort level to apply to the currently selected model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesSaveLargePasteRequest +internal sealed class ModelSetReasoningEffortRequest { - /// Pasted content to save as a UTF-8 file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Reasoning effort level to apply to the currently selected model. The host is responsible for validating the value against the model's supported levels before calling. + [JsonPropertyName("reasoningEffort")] + public string ReasoningEffort { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A single changed file and its unified diff. +/// Availability of a server-advertised routing preference. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspaceDiffFileChange +public sealed class AutoTierStatus { - /// Type of change represented by this file diff. - [JsonPropertyName("changeType")] - public WorkspaceDiffFileChangeType ChangeType { get; set; } - - /// Unified diff content for the file. Empty when the diff was truncated. - [JsonPropertyName("diff")] - public string Diff { get; set; } = string.Empty; - - /// Whether the diff content was omitted because it exceeded the per-file size limit. - [JsonPropertyName("isTruncated")] - public bool? IsTruncated { get; set; } + /// Whether the provider permits selecting this preference. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Original file path for renamed files. - [JsonPropertyName("oldPath")] - public string? OldPath { get; set; } + /// Human-readable explanation of availability. + [JsonPropertyName("message")] + public string? Message { 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; + /// Extensible machine-readable unavailability reason. + [JsonPropertyName("reason")] + public string? Reason { get; set; } } -/// Workspace diff result for the requested mode. +/// A server-advertised routing preference. Identifiers and execution types are extensible. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkspaceDiffResult +public sealed class AutoTierDescriptor { - /// Default branch used for a branch diff, when branch mode was requested. - [JsonPropertyName("baseBranch")] - public string? BaseBranch { get; set; } - - /// Changed files and their unified diffs. - [JsonPropertyName("changes")] - public IList Changes { get => field ??= []; set; } + /// Description displayed beside the preference. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Whether the requested diff fell back to unstaged changes, either because branch diff failed or session diff was unavailable. - [JsonPropertyName("isFallback")] - public bool IsFallback { get; set; } + /// Human-readable label, not a routing identifier. + [JsonPropertyName("displayName")] + public string DisplayName { get; set; } = string.Empty; - /// Effective mode used for the returned changes. - [JsonPropertyName("mode")] - public WorkspaceDiffMode Mode { get; set; } + /// Opaque routing identifier transmitted unchanged to the provider. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Diff mode requested by the client. - [JsonPropertyName("requestedMode")] - public WorkspaceDiffMode RequestedMode { get; set; } + /// Current account-specific availability. + [JsonPropertyName("status")] + public AutoTierStatus Status { get => field ??= new(); 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 kind; this client supports `auto` preferences on the Auto model. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; } -/// Parameters for computing a workspace diff. +/// Account-bound discovery metadata for the virtual `auto` model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class WorkspacesDiffRequest +public sealed class AutoTierMetadata { - /// When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. - [JsonPropertyName("ignoreWhitespace")] - public bool? IgnoreWhitespace { get; set; } + /// Provider-default preference, used only when no explicit preference exists. + [JsonPropertyName("defaultTier")] + public string DefaultTier { get; set; } = string.Empty; - /// Diff mode requested by the client. - [JsonPropertyName("mode")] - public WorkspaceDiffMode Mode { get; set; } + /// Provider that supplied this metadata, when the catalog is provider-attributed. + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Routing preferences in the server's presentation order. + [JsonPropertyName("tiers")] + public IList Tiers { get => field ??= []; set; } } -/// Current per-window credit limit and consumption for an autopilot objective. +/// Cost-category metadata for a CAPI model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutopilotObjectiveCreditLimit +public sealed class SessionModelPriceCategory { - /// Configured AI-credit cap, when one is set. - [JsonPropertyName("credits")] - public double? Credits { get; set; } - - /// Window consumption in fractional AI credits, for display. - [JsonPropertyName("creditsUsed")] - public double CreditsUsed { get; set; } + /// CAPI model identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// Exact window consumption in non-negative integer nano-AIU, encoded as a decimal string. - [RegularExpression("^[0-9]+$")] - [JsonPropertyName("creditsUsedNanoAiu")] - public string CreditsUsedNanoAiu { get; set; } = string.Empty; + /// Cost category assigned to the model. + [JsonPropertyName("priceCategory")] + public ModelPickerPriceCategory PriceCategory { get; set; } } -/// Public, persistence-independent projection of an autopilot objective. +/// 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 AutopilotObjectiveState +public sealed class ModelProviderDescriptor { - /// Optional summary recorded when the objective completed. - [JsonPropertyName("completionSummary")] - public string? CompletionSummary { get; set; } + /// Opaque, stable provider id, stamped onto every model this provider returns. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// 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; + /// The neutral provider kind. + [JsonPropertyName("kind")] + public ModelProviderKind Kind { get; set; } - /// Current per-window consumption and optional cap, when a credit-tracking window is present. - [JsonPropertyName("creditLimit")] - public AutopilotObjectiveCreditLimit? CreditLimit { 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; - /// Session-local objective identifier. - [JsonPropertyName("id")] - public long Id { get; set; } + /// Stable ordering key for presenting providers in a deterministic sequence. + [JsonPropertyName("ordering")] + public long Ordering { get; set; } +} - /// User-provided objective text. - [JsonPropertyName("objective")] - public string Objective { get; set; } = string.Empty; +/// 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; } - /// Optional reason the objective is paused. - [JsonPropertyName("pauseReason")] - public string? PauseReason { 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; } - /// Current normalized lifecycle status. - [JsonPropertyName("status")] - public AutopilotObjectiveStatus Status { 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; } - /// Number of objective turns started. - [JsonPropertyName("turnCount")] - public long TurnCount { 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; } + + /// Per-quota snapshots returned alongside the model list, keyed by quota type. + [JsonPropertyName("quotaSnapshots")] + public IDictionary? QuotaSnapshots { get; set; } } -/// Canonical runtime state for the session's current autopilot objective. +/// RPC data type for SessionModelList operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AutopilotObjectiveGetStateResult +public sealed class SessionModelListRequest { - /// Current objective state, or `null` when the session has no objective. - [JsonPropertyName("state")] - public AutopilotObjectiveState? State { get; 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. +/// RPC data type for SessionModelListRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAutopilotObjectiveGetStateRequest +internal sealed class SessionModelListRequestWithSession { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} -/// Characters that, when typed in the composer, should trigger a `completions.request`. Empty when the session has no host-driven completions (e.g. local sessions, or a relay host that does not advertise `completionTriggerCharacters`). -[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; } + /// 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 SessionCompletionsGetTriggerCharactersRequest +internal sealed class SessionModeGetRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A single host-driven completion. Accepting an item replaces `[rangeStart, rangeEnd)` (UTF-16 code units) in the composer with `insertText`; when the range is absent, the active token around the cursor is replaced. +/// 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 SessionCompletionItem +public sealed class ModeSetResult { - /// Text spliced into the composer when the item is accepted. - [JsonPropertyName("insertText")] - public string InsertText { get; set; } = string.Empty; + /// Whether the host should arm an interactive continuation after the mode change. + [JsonPropertyName("armInteractiveContinuation")] + public bool? ArmInteractiveContinuation { 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; } + /// Compaction confirmation required before the mode change can complete. + [JsonPropertyName("confirmation")] + public ModelSwitchConfirmation? Confirmation { get; set; } - /// Primary display label for the picker row. Falls back to `insertText` when absent. - [JsonPropertyName("label")] - public string? Label { get; set; } + /// Whether the host must defer implementing the requested mode change. + [JsonPropertyName("deferImplementation")] + public bool? DeferImplementation { get; set; } - /// End (exclusive) of the replacement range in `text`, in UTF-16 code units. - [JsonPropertyName("rangeEnd")] - public long? RangeEnd { get; set; } + /// Deprecation warnings associated with the model selected by the mode change. + [JsonPropertyName("deprecationWarnings")] + public IList? DeprecationWarnings { get; set; } - /// Start of the replacement range in `text`, in UTF-16 code units. - [JsonPropertyName("rangeStart")] - public long? RangeStart { get; set; } + /// User-facing outcome message for the model switch triggered by the mode change. + [JsonPropertyName("message")] + public string? Message { get; set; } + + /// Whether the requested mode was applied to the session. False only when an 'expectedMode' precondition did not hold, in which case any model change reported alongside it was still applied. + [JsonPropertyName("modeApplied")] + public bool? ModeApplied { get; set; } + + /// Whether applying the mode changed the active model. + [JsonPropertyName("modelChanged")] + public bool ModelChanged { get; set; } + + /// Lifecycle status of the requested mode change. + [JsonPropertyName("status")] + public string Status { get; set; } = string.Empty; + + /// User-facing warning produced while applying the mode change. + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Host-driven completion items for the current composer input. Empty when the host returns no items or does not support completions. +/// Agent interaction mode to apply to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CompletionsRequestResult +internal sealed class ModeSetRequest { - /// Completion items in host-ranked order. - [JsonPropertyName("items")] - public IList Items { get => field ??= []; set; } + /// 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; } + + /// 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; } -/// Request host-driven completions for the current composer input. +/// The session's friendly name, or null when not yet set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CompletionsRequestRequest +public sealed class NameGetResult { - /// Cursor offset within `text`, in UTF-16 code units. - [JsonPropertyName("offset")] - public long Offset { get; 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 SessionNameGetRequest +{ /// 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; +/// New friendly name to apply to the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class NameSetRequest +{ + /// 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; } -/// Instruction sources loaded for the session, in merge order. +/// Indicates whether the auto-generated summary was applied as the session's name. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class InstructionsGetSourcesResult +public sealed class NameSetAutoResult { - /// Instruction sources for the session. - [JsonPropertyName("sources")] - public IList Sources { 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; } } -/// Identifies the target session. +/// 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 SessionInstructionsGetSourcesRequest +internal sealed class NameSetAutoRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Auto-generated session summary. Empty/whitespace-only values are ignored; values are trimmed before persisting. + [JsonPropertyName("summary")] + public string Summary { get; set; } = string.Empty; +} + +/// Existence, contents, and resolved path of the session plan file. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PlanReadResult +{ + /// The content of the plan file, or null if it does not exist. + [JsonPropertyName("content")] + public string? Content { get; set; } + + /// 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; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionInstructionsReloadRequest +internal sealed class SessionPlanReadRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Diagnostics from reloading skill definitions, with warnings and errors as separate lists. +/// Replacement contents to write to the session plan file. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillsLoadDiagnostics +internal sealed class PlanUpdateRequest { - /// Errors emitted while loading skills (e.g. skills that failed to load entirely). - [JsonPropertyName("errors")] - public IList Errors { get => field ??= []; set; } + /// The new content for the plan file. + [JsonPropertyName("content")] + public string Content { 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; } + /// 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 SessionCustomizationsReloadRequest +internal sealed class SessionPlanDeleteRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether fleet mode was successfully activated. +/// 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 FleetStartResult +public sealed class PlanSqlTodosRow { - /// Whether fleet mode was successfully activated. - [JsonPropertyName("started")] - public bool Started { 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; } } -/// Parameters for starting fleet orchestration: an optional user prompt combined with the fleet instructions, plus the send options forwarded to the resulting turn. +/// Todo rows read from the session SQL database. Empty when no session database is available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class FleetStartRequest +public sealed class PlanReadSqlTodosResult { - /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the fleet request. - [JsonPropertyName("attachments")] - public IList? Attachments { 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; } - - /// Optional user prompt to combine with fleet instructions. - [JsonPropertyName("prompt")] - public string? Prompt { get; set; } + /// Rows from the session SQL todos table, ordered by creation time with insertion order used to break ties when available and id used for WITHOUT ROWID tables. + [JsonPropertyName("rows")] + public IList Rows { get => field ??= []; set; } +} +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionPlanReadSqlTodosRequest +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// 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. +/// 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 AgentList +public sealed class PlanSqlTodoDependency { - /// Available agents. - [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } + /// 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; } -/// RPC data type for SessionAgentList operations. +/// Todo rows + dependency edges read from the session SQL database. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionAgentListRequest +public sealed class PlanReadSqlTodosWithDependenciesResult { - /// 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; } + /// 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; } - /// 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; } + /// 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; } } -/// RPC data type for SessionAgentListRequestWithSession operations. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentListRequestWithSession +internal sealed class SessionPlanReadSqlTodosWithDependenciesRequest { - /// 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; } -/// An in-memory authored prompt override for an available agent. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class AgentSetPromptRequest +/// RPC data type for WorkspacesGetWorkspaceResultWorkspace operations. +public sealed class WorkspacesGetWorkspaceResultWorkspace { - /// Stable effective agent id. Plugin namespace separators are normalized. + /// 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; - /// Replacement authored prompt. Empty text is valid. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Most recent Mission Control event identifier observed for the workspace. + [JsonPropertyName("mc_last_event_id")] + public string? McLastEventId { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } + + /// Whether the workspace session can be steered remotely. + [JsonPropertyName("remote_steerable")] + public bool? RemoteSteerable { 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; } } -/// The currently selected custom agent, or null when using the default agent. +/// Current workspace metadata for the session, including its absolute filesystem path when available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentGetCurrentResult +public sealed class WorkspacesGetWorkspaceResult { - /// Currently selected custom agent, or null if using the default agent. - [JsonPropertyName("agent")] - public AgentInfo? Agent { 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; } + + /// Current workspace metadata, or null if not available. + [JsonPropertyName("workspace")] + public WorkspacesGetWorkspaceResultWorkspace? Workspace { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentGetCurrentRequest +internal sealed class SessionWorkspacesGetWorkspaceRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The newly selected custom agent. +/// Workspace metadata fields to update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentSelectResult +internal sealed class WorkspacesUpdateMetadataRequest { - /// The newly selected custom agent. - [JsonPropertyName("agent")] - public AgentInfo Agent { get => field ??= new(); set; } -} + /// Opaque workspace context supplied by the session host. + [JsonPropertyName("context")] + public JsonElement? Context { 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. + /// Optional workspace display name override. [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + public string? Name { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Optional session context used when creating a local workspace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentDeselectRequest +internal sealed class WorkspacesEnsureRequest { + /// Opaque workspace context supplied by the session host. + [JsonPropertyName("context")] + public JsonElement? Context { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Custom agents available to the session after reloading definitions from disk. +/// Relative paths of files stored in the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AgentReloadResult +public sealed class WorkspacesListFilesResult { - /// Reloaded custom agents. - [JsonPropertyName("agents")] - public IList Agents { get => field ??= []; set; } + /// Slash-separated relative file paths in the workspace files directory. + [JsonPropertyName("files")] + public IList Files { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionAgentReloadRequest +internal sealed class SessionWorkspacesListFilesRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifier assigned to the newly started background agent task. +/// Contents of the requested workspace file as a UTF-8 string. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksStartAgentResult +public sealed class WorkspacesReadFileResult { - /// Generated agent ID for the background task. - [JsonPropertyName("agentId")] - public string AgentId { get; set; } = string.Empty; + /// File content as a UTF-8 string. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; } -/// Agent type, prompt, name, and optional description and model override for the new task. +/// Relative path of the workspace file to read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksStartAgentRequest +internal sealed class WorkspacesReadFileRequest { - /// 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; } + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Optional model override. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Friendly, non-unique name used when displaying the agent. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; +/// Relative path and UTF-8 content for the workspace file to create or overwrite. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class WorkspacesCreateFileRequest +{ + /// File content to write as a UTF-8 string. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Task prompt for the agent. - [JsonPropertyName("prompt")] - public string Prompt { 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; } -/// Tracked task union returned by task APIs, containing an agent, client, or shell task. -/// Polymorphic base type discriminated by type. +/// Filesystem metadata for a path in the session workspace files directory. [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 WorkspacesStatFileResult { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; -} + /// 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; } -/// 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 path identifies a regular file. + [JsonPropertyName("isFile")] + public bool IsFile { get; set; } - /// ISO 8601 timestamp when the current active period began. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("activeStartedAt")] - public DateTimeOffset? ActiveStartedAt { get; set; } + /// Last modification time in Unix epoch milliseconds. + [JsonPropertyName("mtimeMs")] + public double MtimeMs { get; set; } - /// Accumulated active execution time in milliseconds. - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("activeTimeMs")] - public TimeSpan? ActiveTime { get; set; } - - /// Type of agent running this task. - [JsonPropertyName("agentType")] - public required string AgentType { 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; } - - /// ISO 8601 timestamp when the task finished. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } - - /// Short description of the task. - [JsonPropertyName("description")] - public required string Description { get; set; } - - /// 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; } - - /// Whether task execution is synchronously awaited or managed in the background. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("executionMode")] - public TaskExecutionMode? ExecutionMode { get; set; } - - /// Unique task identifier. - [JsonPropertyName("id")] - public required string Id { get; set; } - - /// ISO 8601 timestamp when the agent entered idle state. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("idleSince")] - public DateTimeOffset? IdleSince { get; set; } - - /// Most recent response text from the agent. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("latestResponse")] - public string? LatestResponse { get; set; } - - /// Requested model override for the task when specified. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("model")] - public string? Model { 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; } + /// Size in bytes. + [JsonPropertyName("size")] + public double Size { get; set; } +} - /// Runtime model resolved for the task when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("resolvedModel")] - public string? ResolvedModel { get; set; } +/// Relative path of the workspace file or directory to inspect. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class WorkspacesStatFileRequest +{ + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Result text from the task when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("result")] - public string? Result { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// ISO 8601 timestamp when the task was started. - [JsonPropertyName("startedAt")] - public required DateTimeOffset StartedAt { get; set; } +/// Directory to create within the session workspace files directory. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class WorkspacesCreateDirectoryRequest +{ + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Current lifecycle status of the task. - [JsonPropertyName("status")] - public required TaskStatus Status { get; set; } + /// Whether to create missing parent directories. Defaults to false. + [JsonPropertyName("recursive")] + public bool? Recursive { get; set; } - /// Tool call ID associated with this agent task. - [JsonPropertyName("toolCallId")] - public required string ToolCallId { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Public owner attribution for a client-owned task. Identifiers are opaque and never authorize requests. +/// File or directory to remove from the session workspace files directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskClientOwner +internal sealed class WorkspacesRemovePathRequest { - /// ISO 8601 timestamp when the bound join disconnected. - [JsonPropertyName("disconnectedAt")] - public DateTimeOffset? DisconnectedAt { get; set; } + /// Whether a missing path should be treated as success. Defaults to false. + [JsonPropertyName("force")] + public bool? Force { get; set; } - /// Display-only owner name. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Slash-separated relative path within the workspace files directory. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Opaque identity of the currently or most recently bound session join. - [JsonPropertyName("joinId")] - public string JoinId { get; set; } = string.Empty; + /// Whether to remove directory contents recursively. Defaults to false. + [JsonPropertyName("recursive")] + public bool? Recursive { get; set; } - /// 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; +/// Source and destination paths for a rename within the session workspace files directory. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class WorkspacesRenamePathRequest +{ + /// Slash-separated destination path relative to the workspace files directory. + [JsonPropertyName("destination")] + public string Destination { get; set; } = string.Empty; - /// Whether this task's bound join is currently connected. - [JsonPropertyName("presence")] - public TaskClientOwnerPresence Presence { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Display-only owner source. + /// Slash-separated source path relative to the workspace files directory. [JsonPropertyName("source")] - public string? Source { get; set; } + public string Source { get; set; } = string.Empty; } -/// Tracked client-owned task metadata. -/// The client variant of . +/// Workspace checkpoint metadata with assigned number, human-readable title, and checkpoint filename. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskInfoClient : TaskInfo +public sealed class WorkspacesCheckpoints { - /// - [JsonIgnore] - public override string Type => "client"; - - /// ISO 8601 timestamp when the current active segment started. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("activeStartedAt")] - public DateTimeOffset? ActiveStartedAt { get; set; } - - /// Accumulated active execution time in milliseconds. - [JsonPropertyName("activeTimeMs")] - public required long ActiveTimeMs { get; set; } - - /// Whether the currently bound owner can receive a cancellation request. - [JsonPropertyName("canCancel")] - public required bool CanCancel { get; set; } - - /// Human-readable reason for terminal cancellation. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("cancellationReason")] - public string? CancellationReason { get; set; } - - /// Owner-scoped registration and reclaim key. - [JsonPropertyName("clientTaskId")] - public required string ClientTaskId { get; set; } + /// Filename of the checkpoint within the workspace checkpoints directory. + [JsonPropertyName("filename")] + public string Filename { 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; } + /// Checkpoint number assigned by the workspace manager. + [JsonPropertyName("number")] + public long Number { get; set; } - /// Task description. - [JsonPropertyName("description")] - public required string Description { get; set; } + /// Human-readable checkpoint title. + [JsonPropertyName("title")] + public string Title { get; set; } = string.Empty; +} - /// Optional task display name. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } +/// 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; } +} - /// Human-readable terminal failure message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("error")] - public string? Error { get; 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; +} - /// Optional owner-supplied terminal failure code. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("errorCode")] - public string? ErrorCode { get; set; } +/// Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkspacesReadCheckpointResult +{ + /// Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing. + [JsonPropertyName("content")] + public string? Content { get; set; } +} - /// Execution mode, which is always background for client-owned tasks. - [JsonPropertyName("executionMode")] - public required TaskClientExecutionMode ExecutionMode { get; set; } +/// Checkpoint number to read. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class WorkspacesReadCheckpointRequest +{ + /// Checkpoint number to read. + [JsonPropertyName("number")] + public long Number { get; set; } - /// 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; } +/// Metadata for the persisted summary. +public sealed class WorkspacesAddSummaryResultSummary +{ +} - /// ISO 8601 timestamp of the most recent orphan transition. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("orphanedAt")] - public DateTimeOffset? OrphanedAt { get; set; } +/// Refreshed metadata for the containing workspace. +public sealed class WorkspacesAddSummaryResultWorkspace +{ +} - /// Public attribution and presence for the task owner. - [JsonPropertyName("owner")] - public required TaskClientOwner Owner { 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; } - /// ISO 8601 timestamp of the most recent successful reclaim. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reclaimedAt")] - public DateTimeOffset? ReclaimedAt { get; set; } + /// Refreshed metadata for the containing workspace. + [JsonPropertyName("workspace")] + public WorkspacesAddSummaryResultWorkspace? Workspace { get; set; } +} - /// Opaque successful terminal result supplied by the task owner. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("result")] - public JsonElement? Result { 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; - /// Sequence number of the latest accepted owner update. - [JsonPropertyName("sequence")] - public required long Sequence { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// ISO 8601 timestamp when the task started. - [JsonPropertyName("startedAt")] - public required DateTimeOffset StartedAt { get; set; } + /// Summary title shown in checkpoint listings. + [JsonPropertyName("title")] + public string Title { get; set; } = string.Empty; +} - /// Client task lifecycle status. - [JsonPropertyName("status")] - public required TaskClientStatus Status { get; set; } +/// 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; } - /// 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; } -/// Tracked shell task metadata, including ID, command, status, timing, attachment/execution mode, log path, and PID. -/// The shell variant of . +/// Autopilot objective file content, or null when missing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskInfoShell : TaskInfo +public sealed class WorkspacesReadAutopilotObjectiveResult { - /// - [JsonIgnore] - public override string Type => "shell"; + /// Autopilot objective file content, or null when missing. + [JsonPropertyName("content")] + public string? Content { 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; } +/// 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 this shell task can be promoted to background mode. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("canPromoteToBackground")] - public bool? CanPromoteToBackground { get; set; } +/// Result of writing the autopilot objective file. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkspacesWriteAutopilotObjectiveResult +{ + /// Filesystem operation performed. + [JsonPropertyName("operation")] + public string Operation { get; set; } = string.Empty; +} - /// Command being executed. - [JsonPropertyName("command")] - public required string Command { get; set; } +/// 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; - /// ISO 8601 timestamp when the task finished. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Short description of the task. - [JsonPropertyName("description")] - public required string Description { get; set; } +/// Result of deleting the autopilot objective file. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkspacesDeleteAutopilotObjectiveResult +{ + /// True when a file was deleted. + [JsonPropertyName("deleted")] + public bool Deleted { get; set; } +} - /// Whether task execution is synchronously awaited or managed in the background. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("executionMode")] - public TaskExecutionMode? ExecutionMode { get; set; } +/// 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; +} - /// Unique task identifier. - [JsonPropertyName("id")] - public required string Id { get; set; } +/// Whether the autopilot objective file exists. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkspacesAutopilotObjectiveExistsResult +{ + /// True when the objective file exists. + [JsonPropertyName("exists")] + public bool Exists { get; set; } +} - /// Path to the detached shell log, when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("logPath")] - public string? LogPath { 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; +} - /// Process ID when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("pid")] - public long? Pid { 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; - /// ISO 8601 timestamp when the task was started. - [JsonPropertyName("startedAt")] - public required DateTimeOffset StartedAt { get; set; } + /// Absolute filesystem path to the saved paste file. + [JsonPropertyName("filePath")] + public string FilePath { get; set; } = string.Empty; - /// Current lifecycle status of the task. - [JsonPropertyName("status")] - public required TaskStatus Status { get; set; } + /// Size of the saved file in bytes. + [JsonPropertyName("sizeBytes")] + public long SizeBytes { get; set; } } -/// Background tasks currently tracked by the session. +/// Descriptor for the saved paste file, or null when the workspace is unavailable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskList +public sealed class WorkspacesSaveLargePasteResult { - /// Currently tracked tasks. - [JsonPropertyName("tasks")] - public IList Tasks { get => field ??= []; set; } + /// Saved-paste descriptor, or null when the workspace is unavailable (e.g. CCA runtime, non-infinite sessions, remote sessions). + [JsonPropertyName("saved")] + public WorkspacesSaveLargePasteResultSaved? Saved { get; set; } } -/// Identifies the target session. +/// Pasted content to save as a UTF-8 file in the session workspace. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksListRequest +internal sealed class WorkspacesSaveLargePasteRequest { + /// Pasted content to save as a UTF-8 file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Tracked client-owned task metadata. +/// 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 TaskClientInfo +public sealed class WorkspaceDiffContents { - /// ISO 8601 timestamp when the current active segment started. - [JsonPropertyName("activeStartedAt")] - public DateTimeOffset? ActiveStartedAt { 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; } - /// Accumulated active execution time in milliseconds. - [JsonPropertyName("activeTimeMs")] - public long ActiveTimeMs { get; set; } - - /// Whether the currently bound owner can receive a cancellation request. - [JsonPropertyName("canCancel")] - public bool CanCancel { get; set; } + /// 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; } +} - /// Human-readable reason for terminal cancellation. - [JsonPropertyName("cancellationReason")] - public string? CancellationReason { get; set; } +/// A single changed file and its unified diff. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkspaceDiffFileChange +{ + /// Type of change represented by this file diff. + [JsonPropertyName("changeType")] + public WorkspaceDiffFileChangeType ChangeType { get; set; } - /// Owner-scoped registration and reclaim key. - [JsonPropertyName("clientTaskId")] - public string ClientTaskId { get; set; } = string.Empty; + /// 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; } - /// ISO 8601 timestamp when the task reached a terminal status. - [JsonPropertyName("completedAt")] - public DateTimeOffset? CompletedAt { get; set; } + /// Unified diff content for the file. Empty when the diff was truncated. + [JsonPropertyName("diff")] + public string Diff { get; set; } = string.Empty; - /// Task description. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// Whether the diff content was omitted because it exceeded the per-file size limit. + [JsonPropertyName("isTruncated")] + public bool? IsTruncated { get; set; } - /// Optional task display name. - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + /// Original file path for renamed files. + [JsonPropertyName("oldPath")] + public string? OldPath { get; set; } - /// Human-readable terminal failure message. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Optional owner-supplied terminal failure code. - [JsonPropertyName("errorCode")] - public string? ErrorCode { get; set; } - - /// Execution mode, which is always background for client-owned tasks. - [JsonPropertyName("executionMode")] - public TaskClientExecutionMode ExecutionMode { get; set; } - - /// Canonical runtime-generated task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// ISO 8601 timestamp when the connected owner entered idle status. - [JsonPropertyName("idleSince")] - public DateTimeOffset? IdleSince { get; set; } - - /// ISO 8601 timestamp of the most recent orphan transition. - [JsonPropertyName("orphanedAt")] - public DateTimeOffset? OrphanedAt { get; set; } - - /// Public attribution and presence for the task owner. - [JsonPropertyName("owner")] - public TaskClientOwner Owner { get => field ??= new(); set; } - - /// ISO 8601 timestamp of the most recent successful reclaim. - [JsonPropertyName("reclaimedAt")] - public DateTimeOffset? ReclaimedAt { get; set; } + /// 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; +} - /// Opaque successful terminal result supplied by the task owner. - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } +/// Workspace diff result for the requested mode. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class WorkspaceDiffResult +{ + /// Default branch used for a branch diff, when branch mode was requested. + [JsonPropertyName("baseBranch")] + public string? BaseBranch { get; set; } - /// Sequence number of the latest accepted owner update. - [JsonPropertyName("sequence")] - public long Sequence { get; set; } + /// Changed files and their unified diffs. + [JsonPropertyName("changes")] + public IList Changes { get => field ??= []; set; } - /// ISO 8601 timestamp when the task started. - [JsonPropertyName("startedAt")] - public DateTimeOffset StartedAt { 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; } - /// Client task lifecycle status. - [JsonPropertyName("status")] - public TaskClientStatus Status { get; set; } + /// Effective mode used for the returned changes. + [JsonPropertyName("mode")] + public WorkspaceDiffMode Mode { get; set; } - /// Task kind. - [JsonPropertyName("type")] - public TaskClientType Type { get; set; } + /// Diff mode requested by the client. + [JsonPropertyName("requestedMode")] + public WorkspaceDiffMode RequestedMode { get; set; } - /// ISO 8601 timestamp of the latest accepted lifecycle change. - [JsonPropertyName("updatedAt")] - public DateTimeOffset UpdatedAt { 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; } } -/// Result of registering or reclaiming a client-owned task. +/// Parameters for computing a workspace diff. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksRegisterResult +public sealed class WorkspacesDiffRequest { - /// True only when this invocation created a new task. - [JsonPropertyName("created")] - public bool Created { get; set; } + /// When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. + [JsonPropertyName("ignoreWhitespace")] + public bool? IgnoreWhitespace { get; set; } - /// True only when this invocation reclaimed an orphaned task. - [JsonPropertyName("reclaimed")] - public bool Reclaimed { 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; } - /// Authoritative registered or reclaimed task. - [JsonPropertyName("task")] - public TaskClientInfo Task { get => field ??= new(); set; } + /// Diff mode requested by the client. + [JsonPropertyName("mode")] + public required WorkspaceDiffMode Mode { get; set; } } -/// Registers or reclaims a client-owned task. +/// Parameters for computing a workspace diff. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksRegisterRequest +internal sealed class WorkspacesDiffRequestWithSession { - /// 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; + /// When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. + [JsonPropertyName("ignoreWhitespace")] + public bool? IgnoreWhitespace { get; set; } - /// Optional short display name for the external work. - [JsonPropertyName("displayName")] - public string? DisplayName { 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; } - /// Expected current sequence for idempotent registration or orphan reclaim. - [JsonPropertyName("expectedSequence")] - public long? ExpectedSequence { get; set; } + /// Diff mode requested by the client. + [JsonPropertyName("mode")] + public WorkspaceDiffMode Mode { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// Task kind. - [JsonPropertyName("type")] - public TaskClientType Type { get; set; } } -/// Result of publishing a client-owned task update. +/// Current per-window credit limit and consumption for an autopilot objective. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksUpdateResult +public sealed class AutopilotObjectiveCreditLimit { - /// Whether this invocation changed task state. - [JsonPropertyName("applied")] - public bool Applied { get; set; } + /// Configured AI-credit cap, when one is set. + [JsonPropertyName("credits")] + public double? Credits { get; set; } - /// Whether this invocation repeated the latest accepted update. - [JsonPropertyName("duplicate")] - public bool Duplicate { get; set; } + /// Window consumption in fractional AI credits, for display. + [JsonPropertyName("creditsUsed")] + public double CreditsUsed { get; set; } - /// Authoritative task after processing the update. - [JsonPropertyName("task")] - public TaskClientInfo Task { get => field ??= new(); 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; } -/// Progress or terminal update for a client-owned task. -/// Polymorphic base type discriminated by kind. +/// Public, persistence-independent projection of an autopilot objective. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(TaskClientUpdateProgress), "progress")] -[JsonDerivedType(typeof(TaskClientUpdateCompleted), "completed")] -[JsonDerivedType(typeof(TaskClientUpdateFailed), "failed")] -[JsonDerivedType(typeof(TaskClientUpdateCancelled), "cancelled")] -public partial class TaskClientUpdate +public sealed class AutopilotObjectiveState { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Optional summary recorded when the objective completed. + [JsonPropertyName("completionSummary")] + public string? CompletionSummary { 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; -/// Publishes nonterminal progress for a running or idle client task. -/// The progress variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateProgress : TaskClientUpdate -{ - /// - [JsonIgnore] - public override string Kind => "progress"; + /// Current per-window consumption and optional cap, when a credit-tracking window is present. + [JsonPropertyName("creditLimit")] + public AutopilotObjectiveCreditLimit? CreditLimit { get; set; } - /// Optional progress message appended to recent activity when nonempty. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } + /// Session-local objective identifier. + [JsonPropertyName("id")] + public long Id { get; set; } - /// Optional completion percentage; null clears the current percentage. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("percentage")] - public double? Percentage { get; set; } + /// User-provided objective text. + [JsonPropertyName("objective")] + public string Objective { get; set; } = string.Empty; - /// Optional progress phase; null clears the current phase. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("phase")] - public string? Phase { get; set; } + /// Optional reason the objective is paused. + [JsonPropertyName("pauseReason")] + public string? PauseReason { get; set; } - /// Optional active status transition. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// Current normalized lifecycle status. [JsonPropertyName("status")] - public TaskClientActiveStatus? Status { get; set; } + public AutopilotObjectiveStatus Status { get; set; } + + /// Number of objective turns started. + [JsonPropertyName("turnCount")] + public long TurnCount { get; set; } } -/// Reports successful terminal completion. -/// The completed variant of . +/// Canonical runtime state for the session's current autopilot objective. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateCompleted : TaskClientUpdate +public sealed class AutopilotObjectiveGetStateResult { - /// - [JsonIgnore] - public override string Kind => "completed"; - - /// Optional final progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } - - /// Optional opaque successful terminal result. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("result")] - public JsonElement? Result { get; set; } + /// Current objective state, or `null` when the session has no objective. + [JsonPropertyName("state")] + public AutopilotObjectiveState? State { get; set; } } -/// Reports terminal failure. -/// The failed variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskClientUpdateFailed : TaskClientUpdate +internal sealed class SessionAutopilotObjectiveGetStateRequest { - /// - [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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Reports terminal cancellation after external work stopped. -/// The cancelled 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 TaskClientUpdateCancelled : TaskClientUpdate +public sealed class CompletionsGetTriggerCharactersResult { - /// - [JsonIgnore] - public override string Kind => "cancelled"; - - /// Optional final progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } - - /// Optional human-readable cancellation reason. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// Trigger characters advertised by the host (e.g. `["@", "#"]`). Empty disables host-driven completions for the session. + [JsonPropertyName("triggerCharacters")] + public IList TriggerCharacters { get => field ??= []; set; } } -/// Updates a client-owned task. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksUpdateRequest +internal sealed class SessionCompletionsGetTriggerCharactersRequest { - /// Canonical runtime-generated task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// Owner update sequence to apply. - [JsonPropertyName("sequence")] - public long Sequence { get; set; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; +} - /// Progress or terminal update payload. - [JsonPropertyName("update")] - public TaskClientUpdate Update { get => field ??= new(); set; } +/// A single host-driven completion. Accepting an item replaces `[rangeStart, rangeEnd)` (UTF-16 code units) in the composer with `insertText`; when the range is absent, the active token around the cursor is replaced. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionCompletionItem +{ + /// 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; } + + /// Primary display label for the picker row. Falls back to `insertText` when absent. + [JsonPropertyName("label")] + public string? Label { get; set; } + + /// End (exclusive) of the replacement range in `text`, in UTF-16 code units. + [JsonPropertyName("rangeEnd")] + public long? RangeEnd { get; set; } + + /// Start of the replacement range in `text`, in UTF-16 code units. + [JsonPropertyName("rangeStart")] + public long? RangeStart { get; 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. +/// 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 TasksRefreshResult +public sealed class CompletionsRequestResult { + /// Completion items in host-ranked order. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } } -/// Identifies the target session. +/// Request host-driven completions for the current composer input. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksRefreshRequest +internal sealed class CompletionsRequestRequest { + /// 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; } -/// 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). +/// Instruction sources loaded for the session, in merge order. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksWaitForPendingResult +public sealed class InstructionsGetSourcesResult { + /// Instruction sources for the session. + [JsonPropertyName("sources")] + public IList Sources { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksWaitForPendingRequest +internal sealed class SessionInstructionsGetSourcesRequest { /// 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. +/// Identifies the target session. [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 +internal sealed class SessionInstructionsReloadRequest { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } - -/// Timestamped display line for task progress output or recent agent activity. +/// Result of one customization reload component. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskProgressLine +public sealed class CustomizationReloadOutcome { - /// Display message, e.g., "▸ bash", "✓ edit src/foo.ts". - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// Reason for a skipped component or description of a failure, when available. + [JsonPropertyName("detail")] + public string? Detail { get; set; } - /// ISO 8601 timestamp when this event occurred. - [JsonPropertyName("timestamp")] - public DateTimeOffset Timestamp { get; set; } + /// Whether the component reloaded, was skipped, or failed. + [JsonPropertyName("status")] + public CustomizationReloadStatus Status { get; set; } + + /// Component whose reload was attempted or skipped. + [JsonPropertyName("subsystem")] + public CustomizationReloadSubsystem Subsystem { get; set; } } -/// Progress snapshot for an agent task, with recent activity lines and optional latest intent. -/// The agent 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 TaskProgressAgent : TaskProgress +public sealed class CustomizationsReloadResult { - /// - [JsonIgnore] - public override string Type => "agent"; + /// Errors from any component that could not be refreshed. + [JsonPropertyName("errors")] + public IList Errors { get => field ??= []; set; } - /// The most recent intent reported by the agent. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("latestIntent")] - public string? LatestIntent { 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; } - /// Recent tool execution events converted to display lines. - [JsonPropertyName("recentActivity")] - public required IList RecentActivity { get; set; } + /// Warnings from skill discovery. + [JsonPropertyName("warnings")] + public IList Warnings { get => field ??= []; set; } } -/// Generic progress for a client-owned task. -/// The client variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskProgressClient : TaskProgress +internal sealed class SessionCustomizationsReloadRequest { - /// - [JsonIgnore] - public override string Type => "client"; - - /// Most recent nonempty progress message. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("lastMessage")] - public string? LastMessage { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Current completion percentage from zero through one hundred. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("percentage")] - public double? Percentage { get; set; } +/// Indicates whether fleet mode was successfully activated. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class FleetStartResult +{ + /// Whether fleet mode was successfully activated. + [JsonPropertyName("started")] + public bool Started { get; set; } +} - /// Current owner-defined progress phase. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("phase")] - public string? Phase { get; set; } +/// Parameters for starting fleet orchestration: an optional user prompt combined with the fleet instructions, plus the send options forwarded to the resulting turn. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class FleetStartRequest +{ + /// Optional attachments (files, directories, selections, blobs, GitHub references) to include with the fleet request. + [JsonPropertyName("attachments")] + public IList? Attachments { get; set; } - /// Recent server-timestamped progress messages. - [JsonPropertyName("recentActivity")] - public required IList RecentActivity { 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; } - /// Sequence number of the latest accepted owner update. - [JsonPropertyName("sequence")] - public required long Sequence { get; set; } + /// Optional user prompt to combine with fleet instructions. + [JsonPropertyName("prompt")] + public string? Prompt { get; set; } - /// Current client task lifecycle status. - [JsonPropertyName("status")] - public required TaskClientStatus Status { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// ISO 8601 timestamp of the latest accepted lifecycle change. - [JsonPropertyName("updatedAt")] - public required DateTimeOffset UpdatedAt { get; set; } + /// If true, await completion of the agentic loop for this fleet request before returning. Defaults to false. + [JsonPropertyName("wait")] + public bool? Wait { get; set; } } -/// Progress snapshot for a shell task, with recent stdout/stderr output and optional process ID. -/// The shell variant of . +/// Agents available to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class TaskProgressShell : TaskProgress +public sealed class AgentList { - /// - [JsonIgnore] - public override string Type => "shell"; + /// Available agents. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } +} - /// Process ID when available. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("pid")] - public long? Pid { get; set; } +/// RPC data type for SessionAgentList operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionAgentListRequest +{ + /// When true, request the session's configured built-in agents alongside custom agents. Listing applies feature, context, inclusion, exclusion, and user-disabled-agent policy, but does not evaluate transient invocation requirements such as model availability. Built-in metadata may be omitted when the session cannot project it, such as a relay session. + [JsonPropertyName("includeBuiltInAgents")] + public bool? IncludeBuiltInAgents { get; set; } - /// Recent stdout/stderr lines from the running shell command. - [JsonPropertyName("recentOutput")] - public required string RecentOutput { 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; } } -/// Progress information for the task, or null when no task with that ID is tracked. +/// RPC data type for SessionAgentListRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksGetProgressResult +internal sealed class SessionAgentListRequestWithSession { - /// 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; } + /// 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; } -/// Identifier of the background task to fetch progress for. +/// An in-memory authored prompt override for an available agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksGetProgressRequest +internal sealed class AgentSetPromptRequest { - /// Task identifier (agent ID or shell ID). + /// 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; } -/// The first sync-waiting task that can currently be promoted to background mode. +/// The currently selected custom agent, or null when using the default agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksGetCurrentPromotableResult +public sealed class AgentGetCurrentResult { - /// 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; } + /// 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 SessionTasksGetCurrentPromotableRequest +internal sealed class SessionAgentGetCurrentRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the task was successfully promoted to background mode. +/// The newly selected custom agent. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksPromoteToBackgroundResult +public sealed class AgentSelectResult { - /// Whether the task was successfully promoted to background mode. - [JsonPropertyName("promoted")] - public bool Promoted { get; set; } + /// The newly selected custom agent. + [JsonPropertyName("agent")] + public AgentInfo Agent { get => field ??= new(); set; } } -/// Identifier of the task to promote to background mode. +/// Name of the custom agent to select for subsequent turns. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksPromoteToBackgroundRequest +internal sealed class AgentSelectRequest { - /// Task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// 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; } -/// The promoted task as it now exists in background mode, omitted if no promotable task was waiting. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksPromoteCurrentToBackgroundResult -{ - /// 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; } -} - /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTasksPromoteCurrentToBackgroundRequest +internal sealed class SessionAgentDeselectRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the background task was successfully cancelled. +/// Custom agents available to the session after reloading definitions from disk. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksCancelResult +public sealed class AgentReloadResult { - /// Whether the task was successfully cancelled. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } + /// Reloaded custom agents. + [JsonPropertyName("agents")] + public IList Agents { get => field ??= []; set; } } -/// Identifier of the background task to cancel. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksCancelRequest +internal sealed class SessionAgentReloadRequest { - /// Task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the 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; } -} - -/// Identifier of the completed or cancelled task to remove from tracking. +/// Identifier assigned to the newly started background agent task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TasksRemoveRequest +public sealed class TasksStartAgentResult { - /// Task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Generated agent ID for the background task. + [JsonPropertyName("agentId")] + public string AgentId { get; set; } = string.Empty; } -/// Indicates whether the message was delivered, with an error message when delivery failed. +/// Agent type, prompt, name, and optional description and model override for the new task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TasksSendMessageResult +internal sealed class TasksStartAgentRequest { - /// Error message if delivery failed. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Type of agent to start (e.g., 'explore', 'task', 'general-purpose'). + [JsonPropertyName("agentType")] + public string AgentType { get; set; } = string.Empty; - /// Whether the message was successfully delivered or steered. - [JsonPropertyName("sent")] - public bool Sent { get; set; } -} + /// Short description of the task. + [JsonPropertyName("description")] + public string? Description { 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; } + /// Optional model override. + [JsonPropertyName("model")] + public string? Model { get; set; } - /// Agent task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Friendly, non-unique name used when displaying the agent. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Message content to send to the agent. - [JsonPropertyName("message")] - public string Message { 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; } -/// Skill metadata available to a session, with name, description, source, enabled/invocable state, path, plugin, and argument hint. +/// 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 Skill +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "type", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(TaskInfoAgent), "agent")] +[JsonDerivedType(typeof(TaskInfoClient), "client")] +[JsonDerivedType(typeof(TaskInfoShell), "shell")] +public partial class TaskInfo { - /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. - [JsonPropertyName("argumentHint")] - public string? ArgumentHint { get; set; } + /// The type discriminator. + [JsonPropertyName("type")] + public virtual string Type { get; set; } = string.Empty; +} - /// 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; +/// 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 skill is currently enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// ISO 8601 timestamp when the current active period began. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("activeStartedAt")] + public DateTimeOffset? ActiveStartedAt { get; set; } - /// Unique identifier for the skill. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Accumulated active execution time in milliseconds. + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("activeTimeMs")] + public TimeSpan? ActiveTime { get; set; } - /// Absolute path to the skill file. - [JsonPropertyName("path")] - public string? Path { get; set; } + /// Type of agent running this task. + [JsonPropertyName("agentType")] + public required string AgentType { get; set; } - /// Name of the plugin that provides the skill, when source is 'plugin'. - [JsonPropertyName("pluginName")] - public string? PluginName { 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; } - /// Source location type (e.g., project, personal-copilot, plugin, builtin). - [JsonPropertyName("source")] - public SkillSource Source { get; set; } + /// ISO 8601 timestamp when the task finished. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { get; set; } - /// Whether the skill can be invoked by the user as a slash command. - [JsonPropertyName("userInvocable")] - public bool UserInvocable { get; set; } -} + /// Short description of the task. + [JsonPropertyName("description")] + public required string Description { get; set; } -/// Skills available to the session, with their enabled state. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillList -{ - /// Available skills. - [JsonPropertyName("skills")] - public IList Skills { get => field ??= []; set; } -} + /// Friendly, non-unique name intended for display. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("displayName")] + public string? DisplayName { 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; -} + /// Error message when the task failed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("error")] + public string? Error { get; set; } -/// Skill invocation record with name, path, content, allowed tools, and turn number. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillsInvokedSkill -{ - /// Tools that should be auto-approved when this skill is active, captured at invocation time. - [JsonPropertyName("allowedTools")] - public IList? AllowedTools { get; set; } + /// Whether task execution is synchronously awaited or managed in the background. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("executionMode")] + public TaskExecutionMode? ExecutionMode { get; set; } - /// Full content of the skill file. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Unique task identifier. + [JsonPropertyName("id")] + public required string Id { get; set; } - /// Whether model invocation was disabled when this skill was invoked. - [JsonPropertyName("disableModelInvocation")] - public bool? DisableModelInvocation { get; set; } + /// ISO 8601 timestamp when the agent entered idle state. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("idleSince")] + public DateTimeOffset? IdleSince { get; set; } - /// Turn number when the skill was invoked. - [JsonPropertyName("invokedAtTurn")] - public long InvokedAtTurn { get; set; } + /// Most recent response text from the agent. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("latestResponse")] + public string? LatestResponse { get; set; } - /// Unique identifier for the skill. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Requested model override for the task when specified. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("model")] + public string? Model { 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; -} + /// Most recent prompt delivered to the agent. Updated whenever the agent receives a follow-up message. + [JsonPropertyName("prompt")] + public required string Prompt { get; set; } -/// Skills invoked during this session, ordered by invocation time (most recent last). -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SkillsGetInvokedResult -{ - /// Skills invoked during this session, ordered by invocation time (most recent last). - [JsonPropertyName("skills")] - public IList Skills { get => field ??= []; set; } -} + /// Runtime model resolved for the task when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resolvedModel")] + public string? ResolvedModel { 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; -} + /// Result text from the task when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("result")] + public string? Result { get; set; } -/// Name of the skill to enable for the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsEnableRequest -{ - /// Name of the skill to enable. - [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; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Current lifecycle status of the task. + [JsonPropertyName("status")] + public required TaskStatus Status { get; set; } + + /// Tool call ID associated with this agent task. + [JsonPropertyName("toolCallId")] + public required string ToolCallId { get; set; } } -/// Name of the skill to disable for the session. +/// Public owner attribution for a client-owned task. Identifiers are opaque and never authorize requests. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SkillsDisableRequest +public sealed class TaskClientOwner { - /// Name of the skill to disable. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// ISO 8601 timestamp when the bound join disconnected. + [JsonPropertyName("disconnectedAt")] + public DateTimeOffset? DisconnectedAt { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Display-only owner name. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSkillsReloadRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Opaque identity of the currently or most recently bound session join. + [JsonPropertyName("joinId")] + public string JoinId { get; set; } = string.Empty; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSkillsEnsureLoadedRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Class of the task owner. + [JsonPropertyName("kind")] + public TaskClientOwnerKind Kind { get; set; } -/// Recorded MCP server connection failure. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerFailureInfo -{ - /// Failure message produced when the MCP server connection failed. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// Opaque session-scoped participant identity. + [JsonPropertyName("participantId")] + public string ParticipantId { get; set; } = string.Empty; - /// epoch-ms timestamp at which the failure was recorded. - [JsonPropertyName("timestamp")] - public long Timestamp { get; set; } -} + /// Whether this task's bound join is currently connected. + [JsonPropertyName("presence")] + public TaskClientOwnerPresence Presence { get; set; } -/// Recorded MCP server pending-auth state. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerNeedsAuthInfo -{ - /// epoch-ms timestamp at which the server signalled it needs authentication. - [JsonPropertyName("timestamp")] - public long Timestamp { get; set; } + /// Display-only owner source. + [JsonPropertyName("source")] + public string? Source { get; set; } } -/// Host-level state, omitted when no MCP host is initialized. +/// Tracked client-owned task metadata. +/// The client variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpHostState +public partial class TaskInfoClient : TaskInfo { - /// Names of currently-connected MCP clients. - [JsonPropertyName("clients")] - public IList Clients { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Type => "client"; - /// Configured servers that are explicitly disabled. - [JsonPropertyName("disabledServers")] - public IList DisabledServers { get => field ??= []; set; } + /// ISO 8601 timestamp when the current active segment started. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("activeStartedAt")] + public DateTimeOffset? ActiveStartedAt { get; set; } - /// Map of server name to recorded connection failure. - [JsonPropertyName("failedServers")] - public IDictionary FailedServers { get => field ??= new Dictionary(); set; } + /// Accumulated active execution time in milliseconds. + [JsonPropertyName("activeTimeMs")] + public required long ActiveTimeMs { get; set; } - /// Configured servers filtered out by MCP server policy. - [JsonPropertyName("filteredServers")] - public IList FilteredServers { get => field ??= []; set; } + /// Whether the currently bound owner can receive a cancellation request. + [JsonPropertyName("canCancel")] + public required bool CanCancel { get; set; } - /// Whether third-party MCP servers are policy-enabled for this session. - [JsonPropertyName("mcp3pEnabled")] - public bool Mcp3pEnabled { get; set; } + /// Human-readable reason for terminal cancellation. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cancellationReason")] + public string? CancellationReason { get; set; } - /// Map of server name to recorded pending-auth state. - [JsonPropertyName("needsAuthServers")] - public IDictionary NeedsAuthServers { get => field ??= new Dictionary(); set; } + /// Owner-scoped registration and reclaim key. + [JsonPropertyName("clientTaskId")] + public required string ClientTaskId { get; set; } - /// Names of servers with in-flight connection attempts. - [JsonPropertyName("pendingConnections")] - public IList PendingConnections { get => field ??= []; set; } -} + /// ISO 8601 timestamp when the task reached a terminal status. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { get; set; } -/// Owned installation that a listed MCP server's live configuration came from. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerOwnership -{ - /// Stable installation identifier from the owned installation receipt. - [JsonPropertyName("installationId")] - public string InstallationId { get; set; } = string.Empty; -} + /// Task description. + [JsonPropertyName("description")] + public required string Description { get; set; } -/// MCP server status entry, including config source/plugin source and any connection error. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServer -{ - /// Human-readable display name supplied by a managed server catalog. + /// Optional task display name. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("displayName")] public string? DisplayName { get; set; } - /// Error message if the server failed to connect. + /// Human-readable terminal failure message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [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; + /// Optional owner-supplied terminal failure code. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("errorCode")] + public string? ErrorCode { get; set; } - /// Owned installation this entry's live configuration came from. Absent for manual, workspace, plugin, builtin and same-name servers, and on runtimes without owned installations. - [JsonPropertyName("owned")] - public McpServerOwnership? Owned { get; set; } + /// Execution mode, which is always background for client-owned tasks. + [JsonPropertyName("executionMode")] + public required TaskClientExecutionMode ExecutionMode { 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; } + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public required string Id { get; set; } - /// Configuration source: user, workspace, plugin, builtin, or managed. - [JsonPropertyName("source")] - public McpServerSource? Source { get; set; } + /// ISO 8601 timestamp when the connected owner entered idle status. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("idleSince")] + public DateTimeOffset? IdleSince { get; set; } - /// Plugin name that provided this server, when source is plugin. - [JsonPropertyName("sourcePlugin")] - public string? SourcePlugin { get; set; } + /// ISO 8601 timestamp of the most recent orphan transition. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("orphanedAt")] + public DateTimeOffset? OrphanedAt { get; set; } - /// Plugin version that provided this server, when source is plugin. - [JsonPropertyName("sourcePluginVersion")] - public string? SourcePluginVersion { get; set; } + /// Public attribution and presence for the task owner. + [JsonPropertyName("owner")] + public required TaskClientOwner Owner { get; set; } - /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured. - [JsonPropertyName("status")] - public McpServerStatus Status { get; set; } -} + /// ISO 8601 timestamp of the most recent successful reclaim. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reclaimedAt")] + public DateTimeOffset? ReclaimedAt { get; set; } -/// MCP servers configured for the session, with their connection status and host-level state. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpServerList -{ - /// Host-level state, omitted when no MCP host is initialized. - [JsonPropertyName("host")] - public McpHostState? Host { get; set; } + /// Opaque successful terminal result supplied by the task owner. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } - /// Configured MCP servers. - [JsonPropertyName("servers")] - public IList Servers { get => field ??= []; set; } -} + /// Sequence number of the latest accepted owner update. + [JsonPropertyName("sequence")] + public required long Sequence { get; 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; -} + /// ISO 8601 timestamp when the task started. + [JsonPropertyName("startedAt")] + public required DateTimeOffset StartedAt { get; set; } -/// 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; } + /// Client task lifecycle status. + [JsonPropertyName("status")] + public required TaskClientStatus Status { get; set; } - /// Tool visibility advertised by the server. When absent, MCP Apps defaults apply. - [JsonPropertyName("visibility")] - public IList? Visibility { get; set; } + /// ISO 8601 timestamp of the latest accepted lifecycle change. + [JsonPropertyName("updatedAt")] + public required DateTimeOffset UpdatedAt { get; set; } } -/// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. +/// 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 McpTools +public partial class TaskInfoShell : TaskInfo { - /// Tool description, when provided. + /// + [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; } + + /// ISO 8601 timestamp when the task finished. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { get; set; } + + /// Short description of the task. [JsonPropertyName("description")] - public string? Description { get; set; } + public required string Description { get; set; } - /// Tool name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Whether task execution is synchronously awaited or managed in the background. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("executionMode")] + public TaskExecutionMode? ExecutionMode { 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; } + /// Unique task identifier. + [JsonPropertyName("id")] + public required string Id { get; set; } + + /// Path to the detached shell log, when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("logPath")] + public string? LogPath { get; set; } + + /// Process ID when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pid")] + public long? Pid { get; set; } + + /// ISO 8601 timestamp when the task was started. + [JsonPropertyName("startedAt")] + public required DateTimeOffset StartedAt { get; set; } + + /// Current lifecycle status of the task. + [JsonPropertyName("status")] + public required TaskStatus Status { get; set; } } -/// Tools exposed by the connected MCP server. Throws when the server is not connected. +/// Background tasks currently tracked by the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpListToolsResult +public sealed class TaskList { - /// Tools exposed by the server. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } + /// Currently tracked tasks. + [JsonPropertyName("tasks")] + public IList Tasks { get => field ??= []; set; } } -/// Server name whose tool list should be returned. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpListToolsRequest +internal sealed class SessionTasksListRequest { - /// 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; } -/// Name of the MCP server to enable for the session. +/// Tracked client-owned task metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpEnableRequest +public sealed class TaskClientInfo { - /// Exact receipt identity for explicit owned activation in this session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// ISO 8601 timestamp when the current active segment started. + [JsonPropertyName("activeStartedAt")] + public DateTimeOffset? ActiveStartedAt { 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; } -} + /// Accumulated active execution time in milliseconds. + [JsonPropertyName("activeTimeMs")] + public long ActiveTimeMs { get; set; } -/// Name of the MCP server to enable for the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpEnableRequestWithSession -{ - /// Exact receipt identity for explicit owned activation in this session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// Whether the currently bound owner can receive a cancellation request. + [JsonPropertyName("canCancel")] + public bool CanCancel { 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; + /// Human-readable reason for terminal cancellation. + [JsonPropertyName("cancellationReason")] + public string? CancellationReason { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Owner-scoped registration and reclaim key. + [JsonPropertyName("clientTaskId")] + public string ClientTaskId { 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; } + /// ISO 8601 timestamp when the task reached a terminal status. + [JsonPropertyName("completedAt")] + public DateTimeOffset? CompletedAt { 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; } + /// Task description. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Optional task display name. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } + + /// Human-readable terminal failure message. + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Optional owner-supplied terminal failure code. + [JsonPropertyName("errorCode")] + public string? ErrorCode { get; set; } + + /// Execution mode, which is always background for client-owned tasks. + [JsonPropertyName("executionMode")] + public TaskClientExecutionMode ExecutionMode { get; set; } + + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// ISO 8601 timestamp when the connected owner entered idle status. + [JsonPropertyName("idleSince")] + public DateTimeOffset? IdleSince { get; set; } + + /// ISO 8601 timestamp of the most recent orphan transition. + [JsonPropertyName("orphanedAt")] + public DateTimeOffset? OrphanedAt { get; set; } + + /// Public attribution and presence for the task owner. + [JsonPropertyName("owner")] + public TaskClientOwner Owner { get => field ??= new(); set; } + + /// ISO 8601 timestamp of the most recent successful reclaim. + [JsonPropertyName("reclaimedAt")] + public DateTimeOffset? ReclaimedAt { get; set; } + + /// Opaque successful terminal result supplied by the task owner. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } + + /// Sequence number of the latest accepted owner update. + [JsonPropertyName("sequence")] + public long Sequence { get; set; } + + /// ISO 8601 timestamp when the task started. + [JsonPropertyName("startedAt")] + public DateTimeOffset StartedAt { get; set; } + + /// Client task lifecycle status. + [JsonPropertyName("status")] + public TaskClientStatus Status { get; set; } + + /// Task kind. + [JsonPropertyName("type")] + public TaskClientType Type { get; set; } + + /// ISO 8601 timestamp of the latest accepted lifecycle change. + [JsonPropertyName("updatedAt")] + public DateTimeOffset UpdatedAt { get; set; } } -/// Name of the MCP server to disable for the session. +/// Result of registering or reclaiming a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpDisableRequestWithSession +public sealed class TasksRegisterResult { - /// Required for an owned installation; omission preserves only manual-server behaviour. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// True only when this invocation created a new task. + [JsonPropertyName("created")] + public bool Created { 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; + /// True only when this invocation reclaimed an orphaned task. + [JsonPropertyName("reclaimed")] + public bool Reclaimed { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Authoritative registered or reclaimed task. + [JsonPropertyName("task")] + public TaskClientInfo Task { get => field ??= new(); set; } } -/// Identifies the target session. +/// Registers or reclaims a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpReloadRequest +internal sealed class TasksRegisterRequest { + /// Whether the owner supports runtime cancellation requests. + [JsonPropertyName("cancellable")] + public bool Cancellable { get; set; } + + /// Owner-scoped idempotency key used for registration and reclaim. + [JsonPropertyName("clientTaskId")] + public string ClientTaskId { get; set; } = string.Empty; + + /// Human-readable description of the external work. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Optional short display name for the external work. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } + + /// Expected current sequence for idempotent registration or orphan reclaim. + [JsonPropertyName("expectedSequence")] + public long? ExpectedSequence { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Task kind. + [JsonPropertyName("type")] + public TaskClientType Type { get; set; } } -/// Result of moving in-flight MCP loading to the background. +/// Result of publishing a client-owned task update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MoveMcpLoadingToBackgroundResult +public sealed class TasksUpdateResult { - /// 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; } + /// 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; } } -/// Identifies the target session. +/// Progress or terminal update for a client-owned task. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpMoveLoadingToBackgroundRequest +[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 { - /// 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; } -/// MCP server allowed by policy, with server name and optional PII-free explanatory note. + +/// Publishes nonterminal progress for a running or idle client task. +/// The progress variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAllowedServer +public partial class TaskClientUpdateProgress : TaskClientUpdate { - /// Allowed server name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "progress"; - /// PII-free note explaining why the server was allowed. - [JsonPropertyName("redactedNote")] - public string? RedactedNote { get; set; } + /// 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; } } -/// MCP server whose connection attempt failed. +/// Reports successful terminal completion. +/// The completed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpFailedServer +public partial class TaskClientUpdateCompleted : TaskClientUpdate { - /// The captured connection failure detail. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// + [JsonIgnore] + public override string Kind => "completed"; - /// The config key of the server that failed to connect. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Optional final progress message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } + + /// Optional opaque successful terminal result. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// MCP server filtered by policy, with name, reason, and optional redacted reason. +/// Reports terminal failure. +/// The failed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpFilteredServer +public partial class TaskClientUpdateFailed : TaskClientUpdate { - /// 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")] -#endif - [JsonPropertyName("enterpriseName")] - public string? EnterpriseName { get; set; } + /// + [JsonIgnore] + public override string Kind => "failed"; - /// Filtered server name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Optional owner-supplied terminal failure code. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("code")] + public string? Code { get; set; } - /// Human-readable filter reason. - [JsonPropertyName("reason")] - public string Reason { get; set; } = string.Empty; + /// Human-readable terminal failure message. + [JsonPropertyName("error")] + public required string Error { get; set; } - /// PII-free filter reason. - [JsonPropertyName("redactedReason")] - public string? RedactedReason { get; set; } + /// Optional final progress message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } } -/// MCP server startup filtering result. +/// Reports terminal cancellation after external work stopped. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpStartServersResult +public partial class TaskClientUpdateCancelled : TaskClientUpdate { - /// Non-default servers allowed by policy. - [JsonPropertyName("allowedServers")] - public IList? AllowedServers { get; set; } + /// + [JsonIgnore] + public override string Kind => "cancelled"; - /// Servers whose connection attempt failed. - [JsonPropertyName("failedServers")] - public IList? FailedServers { get; set; } + /// Optional final progress message. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Servers filtered out before startup. - [JsonPropertyName("filteredServers")] - public IList FilteredServers { get => field ??= []; set; } + /// Optional human-readable cancellation reason. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reason")] + public string? Reason { get; set; } } -/// Opaque MCP reload configuration. +/// Updates a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpReloadWithConfigRequest +internal sealed class TasksUpdateRequest { + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Owner update sequence to apply. + [JsonPropertyName("sequence")] + public long Sequence { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Progress or terminal update payload. + [JsonPropertyName("update")] + public TaskClientUpdate Update { get => field ??= new(); set; } } -/// 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. +/// 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 McpExecuteSamplingResult +public sealed class TasksRefreshResult { } -/// Outcome of an MCP sampling execution: success result, failure error, or cancellation. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpSamplingExecutionResult +internal sealed class SessionTasksRefreshRequest { - /// Outcome of the sampling inference. 'success' produced a response; 'failure' encountered an error (including agent-side rejection by content filter or criteria); 'cancelled' the caller cancelled this execution via cancelSamplingExecution. - [JsonPropertyName("action")] - public McpSamplingExecutionAction Action { get; set; } - - /// Error description, present when action='failure'. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// MCP CreateMessageResult payload (with optional 'tools' extension), present when action='success'. Treated as opaque at the schema layer; consumers should construct/consume it per the MCP CreateMessageResult shape. - [JsonPropertyName("result")] - public McpExecuteSamplingResult? Result { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Raw MCP CreateMessageRequest params, as received in the `sampling.requested` event. Treated as opaque at the schema layer; the runtime converts the embedded MCP messages into the OpenAI chat-completion shape internally. +/// 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 McpExecuteSamplingRequest +public sealed class TasksWaitForPendingResult { } -/// 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 SessionTasksWaitForPendingRequest { - /// 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. +/// 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)] -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; } +[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; } -/// The requestId previously passed to executeSampling that should be cancelled. + +/// Timestamped display line for task progress output or recent agent activity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpCancelSamplingExecutionParams +public sealed class TaskProgressLine { - /// The requestId previously passed to executeSampling that should be cancelled. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Display message, e.g., "▸ bash", "✓ edit src/foo.ts". + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// ISO 8601 timestamp when this event occurred. + [JsonPropertyName("timestamp")] + public DateTimeOffset Timestamp { get; set; } } -/// Env-value mode recorded on the session after the update. +/// 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 McpSetEnvValueModeResult +public partial class TaskProgressAgent : TaskProgress { - /// Mode recorded on the session after the update. - [JsonPropertyName("mode")] - public McpSetEnvValueModeDetails Mode { get; set; } + /// + [JsonIgnore] + public override string Type => "agent"; + + /// 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; } } -/// Mode controlling how MCP server env values are resolved (`direct` or `indirect`). +/// Generic progress for a client-owned task. +/// The client variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpSetEnvValueModeParams +public partial class TaskProgressClient : TaskProgress { - /// How environment-variable values supplied to MCP servers are resolved. "direct" passes literal string values; "indirect" treats values as references (e.g. names of environment variables on the host) that the runtime resolves before launch. Defaults to the runtime's startup mode; clients that intentionally launch MCP servers with literal values (e.g. CLI prompt mode and ACP) set this to "direct". - [JsonPropertyName("mode")] - public McpSetEnvValueModeDetails Mode { get; set; } + /// + [JsonIgnore] + public override string Type => "client"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } + + /// Recent server-timestamped progress messages. + [JsonPropertyName("recentActivity")] + public required IList RecentActivity { get; set; } + + /// Sequence number of the latest accepted owner update. + [JsonPropertyName("sequence")] + public required long Sequence { get; set; } + + /// Current client task lifecycle status. + [JsonPropertyName("status")] + public required TaskClientStatus Status { get; set; } + + /// ISO 8601 timestamp of the latest accepted lifecycle change. + [JsonPropertyName("updatedAt")] + public required DateTimeOffset UpdatedAt { get; set; } } -/// Indicates whether the auto-managed `github` MCP server was removed (false when nothing to remove). +/// 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 McpRemoveGitHubResult +public partial class TaskProgressShell : TaskProgress { - /// 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; } + /// + [JsonIgnore] + public override string Type => "shell"; + + /// Process ID when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("pid")] + public long? Pid { get; set; } + + /// Recent stdout/stderr lines from the running shell command. + [JsonPropertyName("recentOutput")] + public required string RecentOutput { get; set; } } -/// Identifies the target session. +/// Progress information for the task, or null when no task with that ID is tracked. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpRemoveGitHubRequest +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; } +} + +/// 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; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of configuring GitHub MCP. +/// The first sync-waiting task that can currently be promoted to background mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigureGitHubResult +public sealed class TasksGetCurrentPromotableResult { - /// Whether GitHub MCP configuration changed. - [JsonPropertyName("changed")] - public bool Changed { get; set; } + /// The first sync-waiting task (agent first, then shell) that can currently be promoted to background mode. Omitted if no such task exists. The returned task is guaranteed to have executionMode='sync' and canPromoteToBackground=true at the time of the call. + [JsonPropertyName("task")] + public TaskInfo? Task { get; set; } } -/// Credential-free authentication identity used to configure GitHub MCP. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpConfigureGitHubRequest +internal sealed class SessionTasksGetCurrentPromotableRequest { /// 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. +/// Indicates whether the task was successfully promoted to background mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpStartServerRequest +public sealed class TasksPromoteToBackgroundResult { - /// 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; } + /// Whether the task was successfully promoted to background mode. + [JsonPropertyName("promoted")] + public bool Promoted { 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. +/// Identifier of the task to promote to background mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpStartServerRequestWithSession +internal sealed class TasksPromoteToBackgroundRequest { - /// 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; + /// Task identifier. + [JsonPropertyName("id")] + public string Id { 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. +/// The promoted task as it now exists in background mode, omitted if no promotable task was waiting. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpRestartServerRequest +public sealed class TasksPromoteCurrentToBackgroundResult { - /// 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; } + /// 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; } } -/// 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpRestartServerRequestWithSession +internal sealed class SessionTasksPromoteCurrentToBackgroundRequest { - /// 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. +/// Indicates whether the background task was successfully cancelled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpStopServerRequest +public sealed class TasksCancelResult { - /// 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; } + /// Whether the task was successfully cancelled. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } -/// Server name for an individual MCP server stop. +/// Identifier of the background task to cancel. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpStopServerRequestWithSession +internal sealed class TasksCancelRequest { - /// 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; + /// Task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Registration parameters for an external MCP client. +/// 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 McpRegisterExternalClientRequest +public sealed class TasksRemoveResult { - /// 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; + /// 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; } } -/// Server name identifying the external client to remove. +/// Identifier of the completed or cancelled task to remove from tracking. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpUnregisterExternalClientRequest +internal sealed class TasksRemoveRequest { - /// Server name of the external client to unregister. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Whether the named MCP server is running. +/// Indicates whether the message was delivered, with an error message when delivery failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpIsServerRunningResult +public sealed class TasksSendMessageResult { - /// True if the server has an active client and transport. - [JsonPropertyName("running")] - public bool Running { get; set; } + /// Error message if delivery failed. + [JsonPropertyName("error")] + public string? Error { get; set; } + + /// Whether the message was successfully delivered or steered. + [JsonPropertyName("sent")] + public bool Sent { get; set; } } -/// Server name to check running status for. +/// Identifier of the target agent task, message content, and optional sender agent ID. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpIsServerRunningRequest +internal sealed class TasksSendMessageRequest { - /// Name of the MCP server to check. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// Agent ID of the sender, if sent on behalf of another agent. + [JsonPropertyName("fromAgentId")] + public string? FromAgentId { get; set; } + + /// Agent task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Message content to send to the agent. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the pending MCP OAuth response was accepted. +/// 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 McpOauthHandlePendingResult +public sealed class Skill { - /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Optional freeform hint describing the skill's expected arguments, from the `argument-hint` frontmatter field. + [JsonPropertyName("argumentHint")] + public string? ArgumentHint { get; set; } + + /// Canonical slash command name used to invoke the skill, without the leading '/'. + [JsonPropertyName("commandName")] + public string? CommandName { get; set; } + + /// Description of what the skill does. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + /// Whether the skill is currently enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// Unique identifier for the skill. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Absolute path to the skill file. + [JsonPropertyName("path")] + public string? Path { get; set; } + + /// Name of the plugin that provides the skill, when source is 'plugin'. + [JsonPropertyName("pluginName")] + public string? PluginName { 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; } } -/// Host response to the pending OAuth request. -/// Polymorphic base type discriminated by kind. +/// Skills available to the session, with their enabled state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpOauthPendingRequestResponseToken), "token")] -[JsonDerivedType(typeof(McpOauthPendingRequestResponseCancelled), "cancelled")] -public partial class McpOauthPendingRequestResponse +public sealed class SkillList { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { 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 SessionSkillsListRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} -/// The token variant of . +/// Skill invocation record with name, path, content, allowed tools, and turn number. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthPendingRequestResponseToken : McpOauthPendingRequestResponse +public sealed class SkillsInvokedSkill { - /// - [JsonIgnore] - public override string Kind => "token"; + /// Tools that should be auto-approved when this skill is active, captured at invocation time. + [JsonPropertyName("allowedTools")] + public IList? AllowedTools { get; set; } - /// Access token acquired by the SDK host. - [JsonPropertyName("accessToken")] - public required string AccessToken { get; set; } + /// Full content of the skill file. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; - /// Token lifetime in seconds, if known. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("expiresIn")] - public long? ExpiresIn { get; set; } + /// Whether model invocation was disabled when this skill was invoked. + [JsonPropertyName("disableModelInvocation")] + public bool? DisableModelInvocation { get; set; } - /// OAuth token type. Defaults to bearer when omitted. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("tokenType")] - public string? TokenType { get; set; } + /// Turn number when the skill was invoked. + [JsonPropertyName("invokedAtTurn")] + public long InvokedAtTurn { get; set; } + + /// Unique identifier for the skill. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// 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; } -/// The cancelled variant of . +/// Skills invoked during this session, ordered by invocation time (most recent last). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthPendingRequestResponseCancelled : McpOauthPendingRequestResponse +public sealed class SkillsGetInvokedResult { - /// - [JsonIgnore] - public override string Kind => "cancelled"; + /// Skills invoked during this session, ordered by invocation time (most recent last). + [JsonPropertyName("skills")] + public IList Skills { get => field ??= []; set; } } -/// Pending MCP OAuth request ID and host-provided token or cancellation response. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthHandlePendingRequest +internal sealed class SessionSkillsGetInvokedRequest { - /// 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; +} - /// Host response to the pending OAuth request. - [JsonPropertyName("result")] - public McpOauthPendingRequestResponse Result { get => field ??= new(); set; } +/// Name of the skill to enable for the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +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; } -/// Identifies the MCP server whose persisted OAuth credentials were updated. +/// Name of the skill to disable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthAuthenticationStateChangedRequest +internal sealed class SkillsDisableRequest { - /// 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; } + /// Name of the skill to disable. + [JsonPropertyName("name")] + public string Name { 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. +/// Diagnostics from reloading skill definitions, with warnings and errors as separate lists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionMcpOauthPrepareLoginResult +public sealed class SkillsLoadDiagnostics { - /// Original expiry, not extended by consumption, retries or cancellation. - [JsonPropertyName("expiresAt")] - public DateTimeOffset ExpiresAt { get; set; } + /// Errors emitted while loading skills (e.g. skills that failed to load entirely). + [JsonPropertyName("errors")] + public IList Errors { get => field ??= []; set; } - /// Retain with the original requester and use for one login or cancellation. - [JsonPropertyName("loginId")] - public string LoginId { 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; } } -/// Effect-free preparation bound to the existing local session, requester and installation, with frozen options. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpOauthPrepareLoginRequest +internal sealed class SessionSkillsReloadRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// 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; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionSkillsEnsureLoadedRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Exact installation identity from owned inventory, never a server-name alias. - [JsonPropertyName("expectedInstallationId")] - public string ExpectedInstallationId { get; set; } = string.Empty; +/// The IDE a host is connected to, as reported to the session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionConnectedIdeInfo +{ + /// Display name of the connected IDE, for example `VS Code`. + [JsonPropertyName("ideName")] + public string IdeName { get; set; } = string.Empty; - /// Request a new authorisation rather than accepting a usable cached grant. - [JsonPropertyName("forceReauth")] - public bool? ForceReauth { get; set; } + /// Absolute path of the workspace folder the IDE has open. + [JsonPropertyName("workspaceFolder")] + public string WorkspaceFolder { get; set; } = string.Empty; +} - /// Name recorded by the authoritative owned installation receipt. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; +/// Records which IDE the host is connected to, or clears it. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionMcpSetConnectedIdeInfoParams +{ + /// 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; } -/// OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server. +/// Recorded MCP server connection failure. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthLoginResult +public sealed class McpServerFailureInfo { - /// 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. When present, the runtime starts the callback listener before returning and continues the flow in the background; completion is signaled via session.mcp_server_status_changed. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("authorizationUrl")] - public string? AuthorizationUrl { get; set; } + /// Failure message produced when the MCP server connection failed. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// Runtime-issued owned flow identity; never a server name or installation operation ID. - [JsonPropertyName("loginId")] - public string? LoginId { get; set; } + /// epoch-ms timestamp at which the failure was recorded. + [JsonPropertyName("timestamp")] + public long Timestamp { get; set; } +} - /// Explicit outcome for owned sign-in. Manual callers retain their legacy response shape. - [JsonPropertyName("status")] - public McpOwnedOauthLoginStatus? Status { get; set; } +/// Recorded MCP server pending-auth state. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpServerNeedsAuthInfo +{ + /// epoch-ms timestamp at which the server signalled it needs authentication. + [JsonPropertyName("timestamp")] + public long Timestamp { get; set; } } -/// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback success-page copy, and static OAuth client selection. +/// Host-level state, omitted when no MCP host is initialized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthLoginRequest +public sealed class McpHostState { - /// 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; } + /// Names of currently-connected MCP clients. + [JsonPropertyName("clients")] + public IList Clients { get => field ??= []; 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 servers that are explicitly disabled. + [JsonPropertyName("disabledServers")] + public IList DisabledServers { get => field ??= []; 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; } + /// Map of server name to recorded connection failure. + [JsonPropertyName("failedServers")] + public IDictionary FailedServers { get => field ??= new Dictionary(); 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; } + /// Configured servers filtered out by MCP server policy. + [JsonPropertyName("filteredServers")] + public IList FilteredServers { get => field ??= []; set; } - /// Exact owned receipt identity. Owned login never uses an implicit helper session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// Whether third-party MCP servers are policy-enabled for this session. + [JsonPropertyName("mcp3pEnabled")] + public bool Mcp3pEnabled { 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; } + /// Map of server name to recorded pending-auth state. + [JsonPropertyName("needsAuthServers")] + public IDictionary NeedsAuthServers { get => field ??= new Dictionary(); 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; } + /// Names of servers with in-flight connection attempts. + [JsonPropertyName("pendingConnections")] + public IList PendingConnections { get => field ??= []; 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; } +/// Owned installation that a listed MCP server's live configuration came from. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpServerOwnership +{ + /// Stable installation identifier from the owned installation receipt. + [JsonPropertyName("installationId")] + public string InstallationId { get; set; } = string.Empty; +} - /// 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; } +/// 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; } - /// Name of the remote MCP server to authenticate. + /// 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("serverName")] - public required string ServerName { get; set; } -} - -/// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback success-page copy, 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; } + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// 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; } + /// 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; } - /// 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; } + /// 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; } - /// 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; } + /// Configuration source: user, workspace, plugin, builtin, or managed. + [JsonPropertyName("source")] + public McpServerSource? Source { get; set; } - /// Exact owned receipt identity. Owned login never uses an implicit helper session. - [JsonPropertyName("expectedInstallationId")] - public string? ExpectedInstallationId { get; set; } + /// Plugin name that provided this server, when source is plugin. + [JsonPropertyName("sourcePlugin")] + public string? SourcePlugin { 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; } + /// Plugin version that provided this server, when source is plugin. + [JsonPropertyName("sourcePluginVersion")] + public string? SourcePluginVersion { 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; } + /// Connection status: connected, failed, needs-auth, pending, disabled, stopped, or not_configured. + [JsonPropertyName("status")] + public McpServerStatus Status { 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; } + /// 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; } +} - /// 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; } +/// MCP servers configured for the session, with their connection status and host-level state. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpServerList +{ + /// Host-level state, omitted when no MCP host is initialized. + [JsonPropertyName("host")] + public McpHostState? Host { 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; + /// 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; } -/// 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. +/// Observational state for a matching already materialized MCP server. [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 +public sealed class McpConfiguredServerState { - /// The type discriminator. + /// 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 virtual string Status { get; set; } = string.Empty; + public McpServerStatus Status { get; set; } } - -/// The no-auth-required variant of . +/// Effective MCP configuration entry. Configuration enablement is distinct from the optional live observation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultNoAuthRequired : McpOauthProbeResult +public sealed class McpConfiguredServer { - /// - [JsonIgnore] - public override string Status => "no-auth-required"; + /// Human-readable display name supplied by configuration. + [JsonPropertyName("displayName")] + public string? DisplayName { get; set; } - /// HTTP response returned by the server. - [JsonPropertyName("httpResponse")] - public required McpOauthHttpResponse HttpResponse { get; set; } + /// Whether this configured server is enabled after session configuration and policy filtering. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// Observed state from an already materialized matching server. Omitted when no live graph has this configured server; it never determines configuration enablement. + [JsonPropertyName("live")] + public McpConfiguredServerState Live { get => field ??= new(); set; } + + /// Server name (config key). + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Configuration provenance: user, workspace, plugin, builtin, or managed. + [JsonPropertyName("source")] + public McpServerSource? Source { get; set; } + + /// 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; } } -/// The authenticated variant of . +/// Effective MCP configuration with optional live observations from matching already materialized servers. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultAuthenticated : McpOauthProbeResult +public sealed class McpConfiguredServerList { - /// - [JsonIgnore] - public override string Status => "authenticated"; + /// Effective configured MCP servers. + [JsonPropertyName("servers")] + public IList Servers { get => field ??= []; set; } +} - /// HTTP response returned by the server. - [JsonPropertyName("httpResponse")] - public required McpOauthHttpResponse HttpResponse { get; 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; } -/// The needs-auth variant of . +/// Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultNeedsAuth : McpOauthProbeResult +public sealed class McpToolUi { - /// - [JsonIgnore] - public override string Status => "needs-auth"; + /// 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; } - /// HTTP 401 or 403 response returned by the server. - [JsonPropertyName("httpResponse")] - public required McpOauthHttpResponse HttpResponse { get; set; } + /// Tool visibility advertised by the server. When absent, MCP Apps defaults apply. + [JsonPropertyName("visibility")] + public IList? Visibility { get; set; } +} - /// Why authentication is needed. - [JsonPropertyName("reason")] - public required McpOauthProbeNeedsAuthReason Reason { get; set; } +/// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpTools +{ + /// Tool description, when provided. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// Parsed WWW-Authenticate challenge parameters, when present and parseable. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("wwwAuthenticateParams")] - public McpOauthWWWAuthenticateParams? WwwAuthenticateParams { get; set; } + /// Tool name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` block was present without recognized fields. + [JsonPropertyName("ui")] + public McpToolUi? Ui { get; set; } } -/// The failed variant of . +/// Tools exposed by the connected MCP server. Throws when the server is not connected. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpOauthProbeResultFailed : McpOauthProbeResult +public sealed class McpListToolsResult { - /// - [JsonIgnore] - public override string Status => "failed"; + /// Tools exposed by the server. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } +} - /// Human-readable probe failure detail. - [JsonPropertyName("error")] - public required string Error { get; set; } +/// Server name whose tool list should be returned. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class McpListToolsRequest +{ + /// Name of the connected MCP server whose tools to list. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Remote MCP server name for a passive OAuth status probe. +/// Name of the MCP server to enable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpOauthProbeRequest +public sealed class McpEnableRequest { - /// Exact owned receipt identity; probing never activates a dormant installation. + /// Exact receipt identity for explicit owned activation in this session. [JsonPropertyName("expectedInstallationId")] public string? ExpectedInstallationId { get; set; } - /// Name of the configured remote MCP server to probe. + /// 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)] @@ -15478,15 +16850,15 @@ public sealed class McpOauthProbeRequest public required string ServerName { get; set; } } -/// Remote MCP server name for a passive OAuth status probe. +/// Name of the MCP server to enable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthProbeRequestWithSession +internal sealed class McpEnableRequestWithSession { - /// Exact owned receipt identity; probing never activates a dormant installation. + /// Exact receipt identity for explicit owned activation in this session. [JsonPropertyName("expectedInstallationId")] public string? ExpectedInstallationId { get; set; } - /// Name of the configured remote MCP server to probe. + /// 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)] @@ -15498,410 +16870,350 @@ internal sealed class McpOauthProbeRequestWithSession public string SessionId { get; set; } = string.Empty; } -/// Honest terminal cancellation result; persistence or recovery failures remain RPC errors. +/// Name of the MCP server to disable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionMcpOauthCancelLoginResult +public sealed class McpDisableRequest { - /// True after cancellation settles, false when the original login already connected successfully. - [JsonPropertyName("cancelled")] - public bool Cancelled { get; set; } + /// 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; } } -/// Targets only the original prepared/applying owned login on this exact session requester. +/// Name of the MCP server to disable for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpOauthCancelLoginRequest +internal sealed class McpDisableRequestWithSession { - /// The same authoritative installation identity supplied during preparation. + /// Required for an owned installation; omission preserves only manual-server behaviour. [JsonPropertyName("expectedInstallationId")] - public string ExpectedInstallationId { get; set; } = string.Empty; + public string? ExpectedInstallationId { get; set; } - /// Runtime-issued login handle known before the effectful login request begins. - [JsonPropertyName("loginId")] - public string LoginId { get; set; } = string.Empty; + /// 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; } -/// 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 SessionMcpReloadRequest { - /// 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 moving in-flight MCP loading to the background. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpOauthRespondRequest +public sealed class MoveMcpLoadingToBackgroundResult { - /// OAuth request identifier from the mcp.oauth_required event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// 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 pending MCP headers refresh response was accepted. +/// MCP server allowed by policy, with server name and optional PII-free explanatory note. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpHeadersHandlePendingHeadersRefreshRequestResult +public sealed class McpAllowedServer { - /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Allowed server name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// PII-free note explaining why the server was allowed. + [JsonPropertyName("redactedNote")] + public string? RedactedNote { get; set; } } -/// Host response: supply dynamic headers or decline this refresh. -/// Polymorphic base type discriminated by kind. +/// MCP server whose connection attempt failed. [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 +public sealed class McpFailedServer { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// The captured connection failure detail. + [JsonPropertyName("error")] + public string? Error { get; set; } + /// The config key of the server that failed to connect. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} -/// The headers variant of . +/// MCP server filtered by policy, with name, reason, and optional redacted reason. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpHeadersHandlePendingHeadersRefreshRequestHeaders : McpHeadersHandlePendingHeadersRefreshRequest +public sealed class McpFilteredServer { - /// - [JsonIgnore] - public override string Kind => "headers"; + /// 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; } - /// 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; } + /// Filtered server name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// 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; } -} + /// Human-readable filter reason. + [JsonPropertyName("reason")] + public string Reason { get; set; } = string.Empty; -/// The none variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpHeadersHandlePendingHeadersRefreshRequestNone : McpHeadersHandlePendingHeadersRefreshRequest -{ - /// - [JsonIgnore] - public override string Kind => "none"; + /// PII-free filter reason. + [JsonPropertyName("redactedReason")] + public string? RedactedReason { get; set; } } -/// The error variant of . +/// MCP server startup filtering result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpHeadersHandlePendingHeadersRefreshRequestError : McpHeadersHandlePendingHeadersRefreshRequest +internal sealed class McpStartServersResult { - /// - [JsonIgnore] - public override string Kind => "error"; + /// Non-default servers allowed by policy. + [JsonPropertyName("allowedServers")] + public IList? AllowedServers { get; set; } - /// Host credential broker failure, denial, or revocation reason. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Servers whose connection attempt failed. + [JsonPropertyName("failedServers")] + public IList? FailedServers { get; set; } + + /// Servers filtered out before startup. + [JsonPropertyName("filteredServers")] + public IList FilteredServers { get => field ??= []; set; } } -/// MCP headers refresh request id and the host response. +/// Opaque MCP reload configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpHeadersHandlePendingHeadersRefreshRequestRequest +internal sealed class McpReloadWithConfigRequest { - /// 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. +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsResourceContent +public sealed class McpExecuteSamplingResult { - /// Resource-level metadata (CSP, permissions, etc.). - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } +} - /// Base64-encoded binary content. - [JsonPropertyName("blob")] - public string? Blob { get; set; } +/// Outcome of an MCP sampling execution: success result, failure error, or cancellation. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpSamplingExecutionResult +{ + /// Outcome of the sampling inference. 'success' produced a response; 'failure' encountered an error (including agent-side rejection by content filter or criteria); 'cancelled' the caller cancelled this execution via cancelSamplingExecution. + [JsonPropertyName("action")] + public McpSamplingExecutionAction Action { get; set; } - /// MIME type of the content. - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } - - /// Text content (e.g. HTML). - [JsonPropertyName("text")] - public string? Text { get; set; } + /// Error description, present when action='failure'. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// The resource URI (typically ui://...). - [JsonPropertyName("uri")] - public string Uri { 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. + [JsonPropertyName("result")] + public McpExecuteSamplingResult? Result { get; set; } } -/// Resource contents returned by the MCP server. +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsReadResourceResult +public sealed class McpExecuteSamplingRequest { - /// Resource contents returned by the server. - [JsonPropertyName("contents")] - public IList Contents { get => field ??= []; set; } } -/// MCP server and resource URI to fetch. +/// Identifiers and raw MCP CreateMessageRequest params used to run a sampling inference. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsReadResourceRequest +internal sealed class McpExecuteSamplingParams { - /// 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)] + /// 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; - - /// Resource URI (typically ui://...). - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; } -/// App-callable tools from the named MCP server. +/// Indicates whether an in-flight sampling execution with the given requestId was found and cancelled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsListToolsResult +public sealed class McpCancelSamplingExecutionResult { - /// App-callable tools from the server. - [JsonPropertyName("tools")] - public IList> Tools { get => field ??= []; set; } + /// True if an in-flight execution with the given requestId was found and signalled to cancel. False when no such execution is in flight (already completed, never started, or cancelled by another caller). + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } -/// MCP server to list app-callable tools for. +/// The requestId previously passed to executeSampling that should be cancelled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsListToolsRequest +internal sealed class McpCancelSamplingExecutionParams { - /// **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; + /// 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; } -/// MCP server, tool name, and arguments to invoke from an MCP App view. +/// Env-value mode recorded on the session after the update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsCallToolRequest +public sealed class McpSetEnvValueModeResult { - /// 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; + /// Mode recorded on the session after the update. + [JsonPropertyName("mode")] + public McpSetEnvValueModeDetails Mode { get; set; } +} - /// MCP server hosting the tool. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] - [MinLength(1)] - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; +/// 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; - - /// MCP tool name. - [JsonPropertyName("toolName")] - public string ToolName { get; set; } = string.Empty; } -/// Host context advertised to MCP App guests. +/// Indicates whether the auto-managed `github` MCP server was removed (false when nothing to remove). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsSetHostContextDetails +public sealed class McpRemoveGitHubResult { - /// 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; } + /// 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; } } -/// Host context to advertise to MCP App guests. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpAppsSetHostContextRequest +internal sealed class SessionMcpRemoveGitHubRequest { - /// 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. +/// Result of configuring GitHub MCP. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsHostContext +internal sealed class McpConfigureGitHubResult { - /// Current host context. - [JsonPropertyName("context")] - public McpAppsHostContextDetails Context { get => field ??= new(); set; } + /// Whether GitHub MCP configuration changed. + [JsonPropertyName("changed")] + public bool Changed { get; set; } } -/// Identifies the target session. +/// Credential-free authentication identity used to configure GitHub MCP. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMcpAppsGetHostContextRequest +internal sealed class McpConfigureGitHubRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Capability negotiation snapshot. +/// 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 McpAppsDiagnoseCapability +public sealed class McpStartServerRequest { - /// Whether the runtime advertises `extensions.io.modelcontextprotocol/ui` to MCP servers. - [JsonPropertyName("advertised")] - public bool Advertised { get; set; } + /// MCP server configuration (stdio process or remote HTTP/SSE). Omit to start the server with its already-registered configuration (config-free start-by-name). + [JsonPropertyName("config")] + public JsonElement? Config { get; set; } - /// Whether the MCP_APPS feature flag (or COPILOT_MCP_APPS env override) is on. - [JsonPropertyName("featureFlagEnabled")] - public bool FeatureFlagEnabled { get; set; } + /// Exact receipt identity for explicit owned activation in this session. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// Whether the session has the `mcp-apps` capability. - [JsonPropertyName("sessionHasMcpApps")] - public bool SessionHasMcpApps { get; set; } + /// Name of the MCP server to start. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// What the server returned for this session. +/// Server name and optional configuration for an individual MCP server start. Omit `config` for a config-free start-by-name of an already-configured server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpAppsDiagnoseServer +internal sealed class McpStartServerRequestWithSession { - /// Whether the named server is currently connected. - [JsonPropertyName("connected")] - public bool Connected { get; set; } + /// 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; } - /// Up to 5 tool names with `_meta.ui` for quick inspection. - [JsonPropertyName("sampleToolNames")] - public IList SampleToolNames { get => field ??= []; set; } + /// Exact receipt identity for explicit owned activation in this session. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// Total tools returned by the server's tools/list. - [JsonPropertyName("toolCount")] - public double ToolCount { get; set; } + /// Name of the MCP server to start. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; - /// Tools whose `_meta.ui` is populated (resourceUri and/or visibility set). - [JsonPropertyName("toolsWithUiMeta")] - public double ToolsWithUiMeta { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Diagnostic snapshot of MCP Apps wiring for the named server. +/// 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 McpAppsDiagnoseResult +public sealed class McpRestartServerRequest { - /// Capability negotiation snapshot. - [JsonPropertyName("capability")] - public McpAppsDiagnoseCapability Capability { get => field ??= new(); set; } + /// 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; } - /// What the server returned for this session. - [JsonPropertyName("server")] - public McpAppsDiagnoseServer Server { get => field ??= new(); set; } + /// Exact receipt identity for an explicit owned restart; configuration overrides are refused. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } + + /// Name of the MCP server to restart. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// MCP server to diagnose MCP Apps wiring for. +/// 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 McpAppsDiagnoseRequest +internal sealed class McpRestartServerRequestWithSession { - /// 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)] + /// 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; @@ -15910,250 +17222,213 @@ internal sealed class McpAppsDiagnoseRequest public string SessionId { get; set; } = string.Empty; } -/// MCP resource content with URI, optional MIME type, text or base64 blob, and resource metadata. +/// Server name for an individual MCP server stop. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourceContent +public sealed class McpStopServerRequest { - /// Resource-level metadata (CSP, permissions, etc.). - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } + /// Exact owned receipt identity. Stop also forgets this session's durable activation. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// Base64-encoded binary content. - [JsonPropertyName("blob")] - public string? Blob { get; set; } + /// Name of the MCP server to stop. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } +} - /// MIME type of the content. - [JsonPropertyName("mimeType")] - public string? MimeType { 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; } - /// Text content (e.g. HTML). - [JsonPropertyName("text")] - public string? Text { get; set; } + /// Name of the MCP server to stop. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; - /// The resource URI. - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Resource contents returned by the MCP server. +/// Registration parameters for an external MCP client. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourcesReadResult +internal sealed class McpRegisterExternalClientRequest { - /// Resource contents returned by the server. - [JsonPropertyName("contents")] - public IList Contents { get => field ??= []; set; } + /// 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; } -/// MCP server and resource URI to fetch. +/// Server name identifying the external client to remove. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpResourcesReadRequest +internal sealed class McpUnregisterExternalClientRequest { - /// 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)] + /// 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; - - /// Resource URI. - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; } -/// Standard MCP resource annotations plus preserved non-standard annotation fields. +/// Whether the named MCP server is running. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourceAnnotations +public sealed class McpIsServerRunningResult { - /// 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; } + /// True if the server has an active client and transport. + [JsonPropertyName("running")] + public bool Running { get; set; } } -/// A resource icon descriptor plus preserved non-standard icon fields. +/// Server name to check running status for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourceIcon +internal sealed class McpIsServerRunningRequest { - /// 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; + /// Name of the MCP server to check. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; - /// Theme hint for this icon. - [JsonPropertyName("theme")] - public string? Theme { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// 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`. +/// Indicates whether the pending MCP OAuth response was accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResource +public sealed class McpOauthHandlePendingResult { - /// 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; } + /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + [JsonPropertyName("success")] + public bool Success { get; set; } +} - /// Icons associated with this resource. - [JsonPropertyName("icons")] - public IList? Icons { 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; +} - /// 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; +/// The token variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class McpOauthPendingRequestResponseToken : McpOauthPendingRequestResponse +{ + /// + [JsonIgnore] + public override string Kind => "token"; - /// Resource size in bytes, when known. - [JsonPropertyName("size")] - public long? Size { get; set; } + /// Access token acquired by the SDK host. + [JsonPropertyName("accessToken")] + public required string AccessToken { get; set; } - /// Optional human-readable display title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Token lifetime in seconds, if known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("expiresIn")] + public long? ExpiresIn { get; set; } - /// The resource URI (e.g. ui://... or file:///...). - [JsonPropertyName("uri")] - public string Uri { get; set; } = string.Empty; + /// OAuth token type. Defaults to bearer when omitted. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenType")] + public string? TokenType { get; set; } } -/// One page of resources advertised by the named MCP server. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourcesListResult +public partial class McpOauthPendingRequestResponseCancelled : McpOauthPendingRequestResponse { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "cancelled"; } -/// MCP server whose resources to enumerate. +/// Pending MCP OAuth request ID and host-provided token or cancellation response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpResourcesListRequest +internal sealed class McpOauthHandlePendingRequest { - /// Opaque MCP pagination cursor from a prior `nextCursor` value. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } + /// OAuth request identifier from the mcp.oauth_required event. + [JsonPropertyName("requestId")] + public string RequestId { 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; + /// 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; } -/// 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`. +/// Identifies the MCP server whose persisted OAuth credentials were updated. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourceTemplate +internal sealed class McpOauthAuthenticationStateChangedRequest { - /// 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; + /// 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; } - /// Optional human-readable display title. - [JsonPropertyName("title")] - public string? Title { 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; } - /// An RFC 6570 URI template for constructing resource URIs. - [JsonPropertyName("uriTemplate")] - public string UriTemplate { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// One page of resource templates advertised by the named MCP server. +/// An inert runtime-issued login handle. Preparation alone performs no activation or OAuth work. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpResourcesListTemplatesResult +public sealed class SessionMcpOauthPrepareLoginResult { - /// Opaque cursor for the next page, if the server has more resource templates. - [JsonPropertyName("nextCursor")] - public string? NextCursor { get; set; } + /// Original expiry, not extended by consumption, retries or cancellation. + [JsonPropertyName("expiresAt")] + public DateTimeOffset ExpiresAt { get; set; } - /// Resource templates advertised by the server (proxied MCP `resources/templates/list`). - [JsonPropertyName("resourceTemplates")] - public IList ResourceTemplates { get => field ??= []; set; } + /// Retain with the original requester and use for one login or cancellation. + [JsonPropertyName("loginId")] + public string LoginId { get; set; } = string.Empty; } -/// MCP server whose resource templates to enumerate. +/// Effect-free preparation bound to the existing local session, requester and installation, with frozen options. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class McpResourcesListTemplatesRequest +internal sealed class SessionMcpOauthPrepareLoginRequest { - /// Opaque MCP pagination cursor from a prior `nextCursor` value. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } + /// 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; } - /// Name of the MCP server whose resource templates to enumerate. - [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + /// 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")] - [MinLength(1)] + [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; @@ -16162,2057 +17437,2662 @@ internal sealed class McpResourcesListTemplatesRequest public string SessionId { get; set; } = string.Empty; } -/// MCP diagnostic source configuration. +/// 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 McpDiagnosticSourceConfiguration +public sealed class McpOauthLoginResult { - /// Threshold to apply to MCP diagnostic producers in this session. - [JsonPropertyName("level")] - public DiagnosticLogLevel Level { get; set; } + /// 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; } } -/// Typed diagnostic source configuration. At least one source is required by diagnostics configuration methods. +/// 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 DiagnosticSourcesConfiguration +public sealed class McpOauthLoginRequest { - /// MCP diagnostic capture threshold. Omit to leave the current threshold unchanged. - [JsonPropertyName("mcp")] - public McpDiagnosticSourceConfiguration? Mcp { get; set; } + /// 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; } } -/// Per-source session diagnostics configuration. +/// 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 DiagnosticsConfiguration +internal sealed class McpOauthLoginRequestWithSession { - /// Diagnostic thresholds keyed by supported source. - [JsonPropertyName("sources")] - public DiagnosticSourcesConfiguration Sources { get => field ??= new(); set; } + /// 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; } -/// Patch session diagnostic thresholds for explicitly supplied sources. +/// Host-delivered callback for a runtime-managed MCP OAuth login. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiagnosticsConfigureRequest +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; - - /// 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. +/// 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 = "source", + TypeDiscriminatorPropertyName = "status", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(DiagnosticEntryMcp), "mcp")] -public partial class DiagnosticEntry +[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("source")] - public virtual string Source { get; set; } = string.Empty; + [JsonPropertyName("status")] + public virtual string Status { get; set; } = string.Empty; } -/// MCP-specific detail for a source-discriminated diagnostic entry. +/// The no-auth-required variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpDiagnosticDetails +public partial class McpOauthProbeResultNoAuthRequired : McpOauthProbeResult { - /// 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; } + /// + [JsonIgnore] + public override string Status => "no-auth-required"; - /// Configured MCP server name. - [JsonPropertyName("serverName")] - public string ServerName { get; set; } = string.Empty; + /// HTTP response returned by the server. + [JsonPropertyName("httpResponse")] + public required McpOauthHttpResponse HttpResponse { get; set; } } -/// The mcp variant of . +/// The authenticated variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class DiagnosticEntryMcp : DiagnosticEntry +public partial class McpOauthProbeResultAuthenticated : McpOauthProbeResult { /// [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; } + public override string Status => "authenticated"; - /// Severity of this emitted diagnostic record. - [JsonPropertyName("level")] - public required DiagnosticSeverity Level { get; set; } + /// HTTP response returned by the server. + [JsonPropertyName("httpResponse")] + public required McpOauthHttpResponse HttpResponse { get; set; } +} - /// Human-readable diagnostic summary. - [JsonPropertyName("message")] - public required string Message { 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"; - /// Original byte count when a known-size message or data value was truncated. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("originalBytes")] - public long? OriginalBytes { get; set; } + /// HTTP 401 or 403 response returned by the server. + [JsonPropertyName("httpResponse")] + public required McpOauthHttpResponse HttpResponse { get; set; } - /// UTC RFC 3339 timestamp captured at the diagnostic source. - [JsonPropertyName("timestamp")] - public required string Timestamp { get; set; } + /// Why authentication is needed. + [JsonPropertyName("reason")] + public required McpOauthProbeNeedsAuthReason Reason { get; set; } - /// Whether message or data was truncated to the record-size bound. + /// Parsed WWW-Authenticate challenge parameters, when present and parseable. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("truncated")] - public bool? Truncated { get; set; } + [JsonPropertyName("wwwAuthenticateParams")] + public McpOauthWWWAuthenticateParams? WwwAuthenticateParams { get; set; } } -/// One cursor-addressed page of retained session diagnostics. +/// The failed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class DiagnosticsReadResult +public partial class McpOauthProbeResultFailed : McpOauthProbeResult { - /// Opaque cursor for the next independent read. - [JsonPropertyName("cursor")] - public string Cursor { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Status => "failed"; - /// Whether the requested cursor remained within the retained buffer window. - [JsonPropertyName("cursorStatus")] - public DiagnosticCursorStatus CursorStatus { get; set; } + /// Human-readable probe failure detail. + [JsonPropertyName("error")] + public required string Error { 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; } + /// 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; } +} - /// Retained records beginning at the requested cursor. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; 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; } - /// Whether additional retained records follow this page. - [JsonPropertyName("hasMore")] - public bool HasMore { 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; } } -/// Cursor-based request for session diagnostics. The default limit is 100 (maximum 500); the default waitMs is zero (maximum 30000). +/// Remote MCP server name for a passive OAuth status probe. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class DiagnosticsReadRequest +internal sealed class McpOauthProbeRequestWithSession { - /// Opaque cursor returned by an earlier read. Omit to start at the oldest retained record. - [JsonPropertyName("cursor")] - public string? Cursor { get; set; } + /// Exact owned receipt identity; probing never activates a dormant installation. + [JsonPropertyName("expectedInstallationId")] + public string? ExpectedInstallationId { get; set; } - /// Maximum number of records to return, from 1 through 500. Omit for 100. - [JsonPropertyName("max")] - public long? Max { 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; - - /// 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. +/// Honest terminal cancellation result; persistence or recovery failures remain RPC errors. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorCapabilities +public sealed class SessionMcpOauthCancelLoginResult { - /// 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; } + /// True after cancellation settles, false when the original login already connected successfully. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } -/// Identifies the target session. +/// Targets only the original prepared/applying owned login on this exact session requester. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsGetCapabilitiesRequest +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; } -/// Credential-free identity metadata. +/// Indicates whether the pending MCP OAuth response was accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class AuthIdentityMetadata +public sealed class McpOauthRespondResult { - /// 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; } + /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Session account selection. +/// Pending MCP OAuth request id to respond to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorSessionAccount +internal sealed class McpOauthRespondRequest { - /// 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; } -} + /// OAuth request identifier from the mcp.oauth_required event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; -/// 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. +/// Indicates whether the pending MCP headers refresh response was accepted. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorAuthorizationRequirement +public sealed class McpHeadersHandlePendingHeadersRefreshRequestResult { - /// 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; } + /// Whether the response was accepted. False if the request was unknown, timed out, or already resolved. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Credential-free Connector catalog entry. +/// Host response: supply dynamic headers or decline this refresh. +/// Polymorphic base type discriminated by kind. [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; } +[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; } -/// Validated Connector catalog snapshot cached by the session. + +/// The headers variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorCatalogResult +public partial class McpHeadersHandlePendingHeadersRefreshRequestHeaders : McpHeadersHandlePendingHeadersRefreshRequest { - /// Validated catalog entries in service order. - [JsonPropertyName("connectors")] - public IList Connectors { get => field ??= []; set; } + /// + [JsonIgnore] + public override string Kind => "headers"; - /// Unix epoch milliseconds when this snapshot was accepted. - [JsonPropertyName("refreshedAtMs")] - public long RefreshedAtMs { get; 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; } - /// Monotonically increasing session-local catalog revision. - [JsonPropertyName("revision")] - public long Revision { 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; } } -/// Live status of one session-owned MCP projection. +/// The none variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorRuntimeStatus +public partial class McpHeadersHandlePendingHeadersRefreshRequestNone : McpHeadersHandlePendingHeadersRefreshRequest { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "none"; } -/// Authoritative session connector state. Account IDs are opaque routing identifiers and credentials are never included. +/// The error variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorStatus +public partial class McpHeadersHandlePendingHeadersRefreshRequestError : McpHeadersHandlePendingHeadersRefreshRequest { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "error"; - /// Live MCP status for every Connector-owned runtime server. - [JsonPropertyName("runtimeServers")] - public IList RuntimeServers { get => field ??= []; set; } + /// Host credential broker failure, denial, or revocation reason. + [JsonPropertyName("message")] + public required string Message { get; set; } } -/// Identifies the target session. +/// MCP headers refresh request id and the host response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsGetStatusRequest +internal sealed class McpHeadersHandlePendingHeadersRefreshRequestRequest { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Headers refresh request identifier from mcp.headers_refresh_required. + [JsonPropertyName("requestId")] + public string RequestId { 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; + /// 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; } -/// Typed result of initiating or continuing a Connector connection. -/// Polymorphic base type discriminated by kind. +/// MCP Apps resource content with URI, optional MIME type, text or base64 blob, and resource metadata. [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 McpAppsResourceContent { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// 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; } -/// The service is connected and the session MCP graph was reconciled. -/// The connected variant of . + /// 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 partial class ConnectorConnectResultConnected : ConnectorConnectResult +public sealed class McpAppsReadResourceResult { - /// - [JsonIgnore] - public override string Kind => "connected"; - - /// Fresh authoritative Connector state after MCP reconciliation. - [JsonPropertyName("status")] - public required ConnectorStatus Status { get; set; } + /// Resource contents returned by the server. + [JsonPropertyName("contents")] + public IList Contents { get => field ??= []; set; } } -/// Host-owned consent is required before bounded continuation can complete. -/// The consent_required variant of . +/// MCP server and resource URI to fetch. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ConnectorConnectResultConsentRequired : ConnectorConnectResult +internal sealed class McpAppsReadResourceRequest { - /// - [JsonIgnore] - public override string Kind => "consent_required"; + /// 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; - /// Validated HTTPS consent URL. The runtime does not open it. - [JsonPropertyName("consentUrl")] - public required string ConsentUrl { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Opaque ID accepted by continueConnection. - [JsonPropertyName("continuationId")] - public required string ContinuationId { get; set; } + /// Resource URI (typically ui://...). + [JsonPropertyName("uri")] + public string Uri { get; set; } = string.Empty; } -/// The service is still completing the connection without a consent URL. -/// The pending variant of . +/// App-callable tools from the named MCP server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ConnectorConnectResultPending : ConnectorConnectResult +public sealed class McpAppsListToolsResult { - /// - [JsonIgnore] - public override string Kind => "pending"; - - /// Opaque ID accepted by continueConnection. - [JsonPropertyName("continuationId")] - public required string ContinuationId { get; set; } + /// App-callable tools from the server. + [JsonPropertyName("tools")] + public IList> Tools { get => field ??= []; set; } } -/// Selects one Connector and the pinned host-owned account used for its service and MCP authorization. +/// MCP server to list app-callable tools for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorConnectRequest +internal sealed class McpAppsListToolsRequest { - /// 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; + /// **Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the app from this server only'), the call is rejected when this differs from `serverName`, and rejected outright when missing. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("originServerName")] + public string OriginServerName { get; set; } = string.Empty; - /// Canonical Connector name from the current catalog. - [JsonPropertyName("connectorName")] - public string ConnectorName { 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; } -/// Explicitly bounded continuation of a pending Connector connection. +/// MCP server, tool name, and arguments to invoke from an MCP App view. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ConnectorContinueRequest +internal sealed class McpAppsCallToolRequest { - /// 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; } + /// Tool arguments. + [JsonPropertyName("arguments")] + public IDictionary? Arguments { get; set; } - /// Maximum catalog requests made by this call. Must be between one and the capability limit. - [JsonPropertyName("maxAttempts")] - public int MaxAttempts { 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; - /// Delay in milliseconds between attempts. Must not exceed the capability limit. - [JsonPropertyName("pollIntervalMs")] - public int PollIntervalMs { get; set; } + /// MCP server hosting the tool. + [RegularExpression("^[^\\x00-\\x1f/\\x7f-\\x9f}]+(?:\\/[^\\x00-\\x1f/\\x7f-\\x9f}]+)*$")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MinLength(1)] + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; /// 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; } + /// MCP tool name. + [JsonPropertyName("toolName")] + public string ToolName { get; set; } = string.Empty; } -/// Requests authoritative Connector-to-MCP reconciliation for the pinned account. +/// Host context advertised to MCP App guests. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ConnectorReconcileRequest +public sealed class McpAppsSetHostContextDetails { - /// Opaque account selection ID. It must match the account already pinned to the session, if any. - [JsonPropertyName("accountId")] - public required string AccountId { get; set; } + /// Display modes the host supports. + [JsonPropertyName("availableDisplayModes")] + public IList? AvailableDisplayModes { get; set; } - /// Optional Connector name to reinitialize. Requires the targetedReconcile capability. - [JsonPropertyName("forceConnectorName")] - public string? ForceConnectorName { get; set; } + /// Current display mode (SEP-1865). + [JsonPropertyName("displayMode")] + public McpAppsSetHostContextDetailsDisplayMode? DisplayMode { get; set; } - /// When true, refresh the catalog before reconciling. A disabled Connector API performs no service request. - [JsonPropertyName("refreshCatalog")] - public bool? RefreshCatalog { get; set; } -} + /// BCP-47 locale, e.g. 'en-US'. + [JsonPropertyName("locale")] + public string? Locale { 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; + /// Platform type for responsive design. + [JsonPropertyName("platform")] + public McpAppsSetHostContextDetailsPlatform? Platform { get; set; } - /// Optional Connector name to reinitialize. Requires the targetedReconcile capability. - [JsonPropertyName("forceConnectorName")] - public string? ForceConnectorName { get; set; } + /// UI theme preference per SEP-1865. + [JsonPropertyName("theme")] + public McpAppsSetHostContextDetailsTheme? Theme { get; set; } - /// When true, refresh the catalog before reconciling. A disabled Connector API performs no service request. - [JsonPropertyName("refreshCatalog")] - public bool? RefreshCatalog { get; set; } + /// IANA timezone, e.g. 'America/New_York'. + [JsonPropertyName("timeZone")] + public string? TimeZone { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Host application identifier. + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } } -/// Identifies the target session. +/// Host context to advertise to MCP App guests. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionConnectorsWithdrawProjectionRequest +internal sealed class McpAppsSetHostContextRequest { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Host context advertised to MCP App guests. + [JsonPropertyName("context")] + public McpAppsSetHostContextDetails Context { get => field ??= new(); set; } -/// 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. +/// Current host context. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class Plugin +public sealed class McpAppsHostContextDetails { - /// 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; } + /// Display modes the host supports. + [JsonPropertyName("availableDisplayModes")] + public IList? AvailableDisplayModes { get; set; } - /// Whether enterprise managed settings control this plugin. - [JsonPropertyName("managed")] - public bool? Managed { get; set; } + /// Current display mode (SEP-1865). + [JsonPropertyName("displayMode")] + public McpAppsHostContextDetailsDisplayMode? DisplayMode { get; set; } - /// Enabled state required by enterprise managed settings. - [JsonPropertyName("managedDesiredEnabled")] - public bool? ManagedDesiredEnabled { get; set; } + /// BCP-47 locale, e.g. 'en-US'. + [JsonPropertyName("locale")] + public string? Locale { get; set; } - /// Marketplace the plugin came from. - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; + /// Platform type for responsive design. + [JsonPropertyName("platform")] + public McpAppsHostContextDetailsPlatform? Platform { get; set; } - /// Plugin name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// UI theme preference per SEP-1865. + [JsonPropertyName("theme")] + public McpAppsHostContextDetailsTheme? Theme { get; set; } - /// Runtime plugin provenance, such as "builtin". - [JsonPropertyName("source")] - public string? Source { get; set; } + /// IANA timezone, e.g. 'America/New_York'. + [JsonPropertyName("timeZone")] + public string? TimeZone { get; set; } - /// Installed version. - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Host application identifier. + [JsonPropertyName("userAgent")] + public string? UserAgent { get; set; } } -/// Plugins installed for the session, with their enabled state and version metadata. +/// Current host context advertised to MCP App guests. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PluginList +public sealed class McpAppsHostContext { - /// Installed plugins. - [JsonPropertyName("plugins")] - public IList Plugins { get => field ??= []; set; } + /// 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 SessionPluginsListRequest +internal sealed class SessionMcpAppsGetHostContextRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Plugin source resolved relative to the session's authoritative working directory. +/// Capability negotiation snapshot. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsInstallRequest +public sealed class McpAppsDiagnoseCapability { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the runtime advertises `extensions.io.modelcontextprotocol/ui` to MCP servers. + [JsonPropertyName("advertised")] + public bool Advertised { get; set; } - /// 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; + /// 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; } } -/// Name (or spec) of the plugin to uninstall. +/// What the server returned for this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUninstallRequestWithSession +public sealed class McpAppsDiagnoseServer { - /// 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; } + /// Whether the named server is currently connected. + [JsonPropertyName("connected")] + public bool Connected { 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; + /// Up to 5 tool names with `_meta.ui` for quick inspection. + [JsonPropertyName("sampleToolNames")] + public IList SampleToolNames { get => field ??= []; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } } -/// Name (or spec) of the plugin to update. +/// Diagnostic snapshot of MCP Apps wiring for the named server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsUpdateRequestWithSession +public sealed class McpAppsDiagnoseResult { - /// Plugin name or "plugin@marketplace" spec to update. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Capability negotiation snapshot. + [JsonPropertyName("capability")] + public McpAppsDiagnoseCapability Capability { get => field ??= new(); set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// What the server returned for this session. + [JsonPropertyName("server")] + public McpAppsDiagnoseServer Server { get => field ??= new(); set; } } -/// Plugin names (or specs) to enable in the session's authoritative working directory. +/// MCP server to diagnose MCP Apps wiring for. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsEnableRequest +internal sealed class McpAppsDiagnoseRequest { - /// 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; } + /// 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; } -/// Plugin names (or specs) to disable in the session's authoritative working directory. +/// MCP resource content with URI, optional MIME type, text or base64 blob, and resource metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsDisableRequest +public sealed class McpResourceContent { - /// 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; } + /// 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; } -/// RPC data type for SessionPluginsReload operations. +/// Standard MCP resource annotations plus preserved non-standard annotation fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionPluginsReloadRequest +public sealed class McpResourceAnnotations { - /// 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; } + /// Server-provided non-standard annotation fields preserved from the MCP response. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { 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; } + /// Intended audience roles for this resource. + [JsonPropertyName("audience")] + public IList? Audience { 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; } + /// Last-modified timestamp hint. + [JsonPropertyName("lastModified")] + public string? LastModified { get; set; } - /// Reload MCP server connections after refreshing plugins. Defaults to true. - [JsonPropertyName("reloadMcp")] - public bool? ReloadMcp { get; set; } + /// Priority hint for model/client use. + [JsonPropertyName("priority")] + public double? Priority { get; set; } } -/// RPC data type for SessionPluginsReloadRequestWithSession operations. +/// A resource icon descriptor plus preserved non-standard icon fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsReloadRequestWithSession +public sealed class McpResourceIcon { - /// 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; } + /// Server-provided non-standard icon fields preserved from the MCP response. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { 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; } + /// Icon MIME type, when known. + [JsonPropertyName("mimeType")] + public string? MimeType { 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; } + /// Icon sizes hint. + [JsonPropertyName("sizes")] + public string? Sizes { get; set; } - /// Reload MCP server connections after refreshing plugins. Defaults to true. - [JsonPropertyName("reloadMcp")] - public bool? ReloadMcp { get; set; } + /// Icon URI. + [JsonPropertyName("src")] + public string Src { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Theme hint for this icon. + [JsonPropertyName("theme")] + public string? Theme { get; set; } } -/// Identifies the target session. +/// 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)] -internal sealed class SessionPluginsMarketplacesListRequest +public sealed class McpResource { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } -/// Marketplace source and optional working directory for relative-path resolution. +/// One page of resources advertised by the named MCP server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesAddRequestWithSession +public sealed class McpResourcesListResult { - /// 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; + /// Opaque cursor for the next page, if the server has more resources. + [JsonPropertyName("nextCursor")] + public string? NextCursor { 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; } + /// Resources advertised by the server (proxied MCP `resources/list`). + [JsonPropertyName("resources")] + public IList Resources { get => field ??= []; set; } } -/// Name of the marketplace to remove and an optional force flag. +/// MCP server whose resources to enumerate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PluginsMarketplacesRemoveRequestWithSession +internal sealed class McpResourcesListRequest { - /// 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; } + /// Opaque MCP pagination cursor from a prior `nextCursor` value. + [JsonPropertyName("cursor")] + public string? Cursor { get; set; } - /// Marketplace name to remove. - [JsonPropertyName("name")] - public string Name { 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; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Name of the marketplace whose plugin catalog to fetch. +/// 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)] -internal sealed class PluginsMarketplacesBrowseRequestWithSession +public sealed class McpResourceTemplate { - /// Marketplace name to browse. + /// 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; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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; } -/// RPC data type for SessionPluginsMarketplacesRefresh operations. +/// One page of resource templates advertised by the named MCP server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionPluginsMarketplacesRefreshRequest +public sealed class McpResourcesListTemplatesResult { - /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// 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; } } -/// RPC data type for SessionPluginsMarketplacesRefreshRequestWithSession operations. +/// MCP server whose resource templates to enumerate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionPluginsMarketplacesRefreshRequestWithSession +internal sealed class McpResourcesListTemplatesRequest { - /// Marketplace name to refresh. When omitted, every registered marketplace is refreshed. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// 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; } -/// 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. +/// An argument accepted by an MCP prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderSessionToken +public sealed class McpPromptArgument { - /// 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; } + /// Argument-level metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } - /// HTTP header name the token must be sent under. - [JsonPropertyName("header")] - public string Header { get; set; } = string.Empty; + /// Server-provided non-standard argument fields. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } - /// The model the token is bound to, when applicable. When set, the token is only valid for requests against this model. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Description of the argument. + [JsonPropertyName("description")] + public string? Description { get; set; } - /// The short-lived token value. - [JsonPropertyName("token")] - public string Token { get; set; } = string.Empty; + /// 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; } } -/// A snapshot of the provider endpoint the session is currently configured to talk to. +/// An MCP prompt icon with standard size hints and preserved non-standard fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderEndpoint +public sealed class McpPromptIcon { - /// A credential the caller should use with this endpoint. Omitted only when the endpoint accepts unauthenticated requests. - [JsonPropertyName("apiKey")] - public string? ApiKey { get; set; } + /// Server-provided non-standard icon fields. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } - /// Base URL to pass to the LLM client library. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("baseUrl")] - public string BaseUrl { get; set; } = string.Empty; + /// Icon MIME type, when known. + [JsonPropertyName("mimeType")] + public string? MimeType { get; set; } - /// HTTP headers the caller must include on every outbound request. - [JsonPropertyName("headers")] - public IDictionary Headers { get => field ??= new Dictionary(); set; } + /// Icon sizes, such as `48x48` or `any`. + [JsonPropertyName("sizes")] + public IList? Sizes { get; set; } - /// Short-lived, rotating credential the caller must send on every request, in addition to `apiKey` if one is present. Omitted when the endpoint does not require one. - [JsonPropertyName("sessionToken")] - public ProviderSessionToken? SessionToken { get; set; } + /// Icon URI. + [JsonPropertyName("src")] + public string Src { get; set; } = string.Empty; - /// Transport to be used for provider requests. - [JsonPropertyName("transport")] - public ProviderEndpointTransport? Transport { get; set; } + /// Theme hint for this icon. + [JsonPropertyName("theme")] + public string? Theme { get; set; } +} - /// Provider family. Matches the `type` field of a BYOK provider config. - [JsonPropertyName("type")] - public ProviderEndpointType Type { 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; } - /// Wire API to be used, when required for the provider type. - [JsonPropertyName("wireApi")] - public ProviderEndpointWireApi? WireApi { 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; } } -/// RPC data type for SessionProviderGetEndpoint operations. +/// One page of prompts advertised by the named MCP server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionProviderGetEndpointRequest +public sealed class McpPromptsListResult { - /// 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; } + /// 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; } } -/// RPC data type for SessionProviderGetEndpointRequestWithSession operations. +/// MCP server whose prompts to enumerate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionProviderGetEndpointRequestWithSession +internal sealed class McpPromptsListRequest { - /// 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; } + /// 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; } -/// The selectable model entries synthesized for the models added by this call. +/// An MCP prompt message with opaque JSON content preserved without flattening or content-type filtering. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderAddResult +public sealed class McpPromptMessage { - /// 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; } -} + /// Message-level metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } -/// RPC data type for ProtocolSystemMessageAppendConfig operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolSystemMessageAppendConfig -{ - /// Text appended to the standard system prompt. + /// 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 string? Content { get; set; } + public JsonElement Content { get; set; } - /// Append-mode discriminator. Omission also selects append mode. - [JsonPropertyName("mode")] - public ProtocolAppendMode? Mode { get; set; } + /// The role of the message sender. + [JsonPropertyName("role")] + public McpPromptRole Role { get; set; } } -/// RPC data type for SystemMessageBlock operations. +/// Prompt messages returned by the MCP server without sending them to the model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SystemMessageBlock +public sealed class McpPromptsGetResult { - /// Whether providers with explicit prompt caching should place a cache breakpoint after this block. - [JsonPropertyName("cacheBreakpoint")] - public bool? CacheBreakpoint { get; set; } + /// MCP result metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } - /// Text content for this system-message block. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Server-provided non-standard result fields. + [JsonPropertyName("additionalProperties")] + public IDictionary? AdditionalProperties { get; set; } - /// Whether the block is static and may be cached independently of dynamic prompt content. - [JsonPropertyName("isStatic")] - public bool? IsStatic { get; set; } + /// Description of the prompt. + [JsonPropertyName("description")] + public string? Description { get; set; } + + /// Ordered prompt messages. + [JsonPropertyName("messages")] + public IList Messages { get => field ??= []; set; } } -/// RPC data type for ProtocolSystemMessageReplaceConfig operations. +/// MCP server, prompt name, and optional string-valued arguments. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolSystemMessageReplaceConfig +internal sealed class McpPromptsGetRequest { - /// Complete replacement system-message text. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// String-valued arguments to pass to the prompt. + [JsonPropertyName("arguments")] + public IDictionary? Arguments { get; set; } - /// Optional structured blocks corresponding to the replacement content. - [JsonPropertyName("contentBlocks")] - public IList? ContentBlocks { 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; - /// Replace-mode discriminator. - [JsonPropertyName("mode")] - public ProtocolReplaceMode Mode { 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; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// RPC data type for ProtocolStaticSectionOverride operations. +/// MCP diagnostic source configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolStaticSectionOverride +public sealed class McpDiagnosticSourceConfiguration { - /// Declarative operation applied to the section. - [JsonPropertyName("action")] - public ProtocolStaticSectionAction Action { get; set; } - - /// Optional content used by replace, append, and prepend operations. - [JsonPropertyName("content")] - public string? Content { get; set; } + /// Threshold to apply to MCP diagnostic producers in this session. + [JsonPropertyName("level")] + public DiagnosticLogLevel Level { get; set; } } -/// Polymorphic base type discriminated by action. +/// Typed diagnostic source configuration. At least one source is required by diagnostics configuration methods. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "action", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(ProtocolMarkerSectionOverrideTransform), "transform")] -[JsonDerivedType(typeof(ProtocolMarkerSectionOverridePreserve), "preserve")] -public partial class ProtocolMarkerSectionOverride +public sealed class DiagnosticSourcesConfiguration { - /// The type discriminator. - [JsonPropertyName("action")] - public virtual string Action { get; set; } = string.Empty; + /// MCP diagnostic capture threshold. Omit to leave the current threshold unchanged. + [JsonPropertyName("mcp")] + public McpDiagnosticSourceConfiguration? Mcp { get; set; } } - -/// The transform variant of . +/// Per-source session diagnostics configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ProtocolMarkerSectionOverrideTransform : ProtocolMarkerSectionOverride +public sealed class DiagnosticsConfiguration { - /// - [JsonIgnore] - public override string Action => "transform"; + /// Diagnostic thresholds keyed by supported source. + [JsonPropertyName("sources")] + public DiagnosticSourcesConfiguration Sources { get => field ??= new(); set; } } -/// The preserve variant of . +/// Patch session diagnostic thresholds for explicitly supplied sources. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ProtocolMarkerSectionOverridePreserve : ProtocolMarkerSectionOverride +internal sealed class DiagnosticsConfigureRequest { - /// - [JsonIgnore] - public override string Action => "preserve"; + /// 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; } } -/// JSON union data type for ProtocolSectionOverride. +/// 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)] -[JsonConverter(typeof(Converter))] -public sealed partial class ProtocolSectionOverride -{ - /// Gets the value when this instance contains . - public ProtocolStaticSectionOverride? ProtocolStaticSectionOverride { get; } - - /// Gets the value when this instance contains . - public ProtocolMarkerSectionOverride? ProtocolMarkerSectionOverride { get; } - - /// Initializes a new instance of the class from . - public ProtocolSectionOverride(ProtocolStaticSectionOverride value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolStaticSectionOverride = value; - } - - /// Converts to . - public static implicit operator ProtocolSectionOverride(ProtocolStaticSectionOverride value) => new(value); - - /// Initializes a new instance of the class from . - public ProtocolSectionOverride(ProtocolMarkerSectionOverride value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolMarkerSectionOverride = value; - } - - /// Converts to . - public static implicit operator ProtocolSectionOverride(ProtocolMarkerSectionOverride value) => new(value); - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override ProtocolSectionOverride Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Null) - { - throw new JsonException("Expected JSON object for ProtocolSectionOverride."); - } - - using var document = JsonDocument.ParseValue(ref reader); - var element = document.RootElement; - if (element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("action", out _) && element.GetProperty("action").ValueKind == JsonValueKind.String && (element.GetProperty("action").GetString() == "replace" || element.GetProperty("action").GetString() == "remove" || element.GetProperty("action").GetString() == "append" || element.GetProperty("action").GetString() == "prepend"))) - { - var protocolStaticSectionOverride = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolStaticSectionOverride); - return protocolStaticSectionOverride is null ? throw new JsonException("Expected ProtocolStaticSectionOverride value.") : new ProtocolSectionOverride(protocolStaticSectionOverride); - } - if ((element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("action", out _) && element.GetProperty("action").ValueKind == JsonValueKind.String && (element.GetProperty("action").GetString() == "transform")) || element.ValueKind == JsonValueKind.Object && (element.TryGetProperty("action", out _) && element.GetProperty("action").ValueKind == JsonValueKind.String && (element.GetProperty("action").GetString() == "preserve")))) - { - var protocolMarkerSectionOverride = JsonSerializer.Deserialize(element, RpcJsonContext.Default.ProtocolMarkerSectionOverride); - return protocolMarkerSectionOverride is null ? throw new JsonException("Expected ProtocolMarkerSectionOverride value.") : new ProtocolSectionOverride(protocolMarkerSectionOverride); - } - - throw new JsonException("JSON value did not match any ProtocolSectionOverride variant."); - } - - /// - public override void Write(Utf8JsonWriter writer, ProtocolSectionOverride value, JsonSerializerOptions options) - { - if (value.ProtocolStaticSectionOverride is { } protocolStaticSectionOverride) - { - JsonSerializer.Serialize(writer, protocolStaticSectionOverride, RpcJsonContext.Default.ProtocolStaticSectionOverride); - return; - } - if (value.ProtocolMarkerSectionOverride is { } protocolMarkerSectionOverride) - { - JsonSerializer.Serialize(writer, protocolMarkerSectionOverride, RpcJsonContext.Default.ProtocolMarkerSectionOverride); - return; - } - - throw new JsonException("No ProtocolSectionOverride variant value is set."); - } - } -} - -/// RPC data type for ProtocolSystemMessageCustomizeConfig operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolSystemMessageCustomizeConfig +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "source", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(DiagnosticEntryMcp), "mcp")] +public partial class DiagnosticEntry { - /// 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; } + /// The type discriminator. + [JsonPropertyName("source")] + public virtual string Source { get; set; } = string.Empty; } -/// JSON union data type for ProtocolSystemMessageConfig. + +/// MCP-specific detail for a source-discriminated diagnostic entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -public sealed partial class ProtocolSystemMessageConfig +public sealed class McpDiagnosticDetails { - /// Gets the value when this instance contains . - public ProtocolSystemMessageAppendConfig? ProtocolSystemMessageAppendConfig { get; } - - /// Gets the value when this instance contains . - public ProtocolSystemMessageReplaceConfig? ProtocolSystemMessageReplaceConfig { get; } + /// Fresh identifier for the MCP connection attempt, including failed starts. + [JsonPropertyName("connectionId")] + public string ConnectionId { get; set; } = string.Empty; - /// Gets the value when this instance contains . - public ProtocolSystemMessageCustomizeConfig? ProtocolSystemMessageCustomizeConfig { get; } + /// Serialized diagnostic detail. Protocol and HTTP records use JSON when detail is present. + [JsonPropertyName("data")] + public string? Data { get; set; } - /// Initializes a new instance of the class from . - public ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolSystemMessageAppendConfig = value; - } + /// Protocol-frame direction when kind is protocol. + [JsonPropertyName("direction")] + public McpDiagnosticDirection? Direction { get; set; } - /// Converts to . - public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageAppendConfig value) => new(value); + /// Diagnostic record category. + [JsonPropertyName("kind")] + public McpDiagnosticKind Kind { get; set; } - /// Initializes a new instance of the class from . - public ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolSystemMessageReplaceConfig = value; - } + /// Configured MCP server name. + [JsonPropertyName("serverName")] + public string ServerName { get; set; } = string.Empty; +} - /// Converts to . - public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageReplaceConfig value) => new(value); +/// The mcp variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class DiagnosticEntryMcp : DiagnosticEntry +{ + /// + [JsonIgnore] + public override string Source => "mcp"; - /// Initializes a new instance of the class from . - public ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) - { - ArgumentNullException.ThrowIfNull(value); - ProtocolSystemMessageCustomizeConfig = value; - } + /// Agent identifier for a subagent host. Omitted for the root agent. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } - /// Converts to . - public static implicit operator ProtocolSystemMessageConfig(ProtocolSystemMessageCustomizeConfig value) => new(value); + /// Typed MCP diagnostic detail. + [JsonPropertyName("details")] + public required McpDiagnosticDetails Details { 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."); - } + /// Severity of this emitted diagnostic record. + [JsonPropertyName("level")] + public required DiagnosticSeverity Level { 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); - } + /// Human-readable diagnostic summary. + [JsonPropertyName("message")] + public required string Message { get; set; } - throw new JsonException("JSON value did not match any ProtocolSystemMessageConfig variant."); - } + /// Original byte count when a known-size message or data value was truncated. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("originalBytes")] + public long? OriginalBytes { 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; - } + /// UTC RFC 3339 timestamp captured at the diagnostic source. + [JsonPropertyName("timestamp")] + public required string Timestamp { get; set; } - throw new JsonException("No ProtocolSystemMessageConfig variant value is set."); - } - } + /// Whether message or data was truncated to the record-size bound. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("truncated")] + public bool? Truncated { get; set; } } -/// A BYOK model definition referencing a named provider. +/// One cursor-addressed page of retained session diagnostics. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderModelConfig +public sealed class DiagnosticsReadResult { - /// 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; + /// Opaque cursor for the next independent read. + [JsonPropertyName("cursor")] + public string Cursor { get; set; } = string.Empty; - /// Maximum context window tokens for the model. - [JsonPropertyName("maxContextWindowTokens")] - public double? MaxContextWindowTokens { get; set; } + /// Whether the requested cursor remained within the retained buffer window. + [JsonPropertyName("cursorStatus")] + public DiagnosticCursorStatus CursorStatus { get; set; } - /// Maximum output tokens for the model. - [JsonPropertyName("maxOutputTokens")] - public double? MaxOutputTokens { 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; } - /// Maximum prompt/input tokens for the model. - [JsonPropertyName("maxPromptTokens")] - public double? MaxPromptTokens { get; set; } + /// Retained records beginning at the requested cursor. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } - /// Provider-published model metadata, preserved verbatim as the public Model.metadata object. - [JsonPropertyName("metadata")] - public IDictionary? Metadata { get; set; } + /// Whether additional retained records follow this page. + [JsonPropertyName("hasMore")] + public bool HasMore { get; set; } +} - /// Well-known base model id used for behavior/capability/config lookup. Defaults to `id`. - [JsonPropertyName("modelId")] - public string? ModelId { 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; } - /// Display name for model pickers. Defaults to the provider-qualified selection id (`provider/id`). - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Maximum number of records to return, from 1 through 500. Omit for 100. + [JsonPropertyName("max")] + public long? Max { get; set; } - /// Name of the configured provider that serves this model. - [JsonPropertyName("provider")] - public string Provider { get; set; } = string.Empty; + /// Target 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; } + /// Nonempty selection of sources to read. Each source may be listed once. + [JsonPropertyName("sources")] + public IList Sources { get => field ??= []; set; } - /// The model name sent to the provider API for inference. Defaults to `id`. - [JsonPropertyName("wireModel")] - public string? WireModel { get; set; } + /// Maximum time in milliseconds to wait for a new record, from 0 through 30000. + [JsonPropertyName("waitMs")] + public int? WaitMs { get; set; } } -/// Azure-specific provider options. +/// Feature detection and hard polling limits for the EXPERIMENTAL session connector API. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderConfigAzure +public sealed class ConnectorCapabilities { - /// API version. When set, uses the versioned deployment route. When omitted, uses the GA versionless v1 route. + /// Connector API contract version. [JsonPropertyName("apiVersion")] - public string? ApiVersion { get; set; } -} - -/// 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 NamedProviderConfig -{ - /// 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; } + public long ApiVersion { get; set; } - /// Base URL for provider API requests. - [JsonPropertyName("baseUrl")] - public string BaseUrl { get; set; } = string.Empty; + /// Current session availability. Disabled availability is reported without making a Connector request. + [JsonPropertyName("availability")] + public ConnectorAvailability Availability { get; set; } - /// Static bearer token used to authenticate provider requests. - [JsonPropertyName("bearerToken")] - public string? BearerToken { get; set; } + /// Whether connect and reconnect can return an opaque continuation for bounded consent polling. + [JsonPropertyName("consentContinuation")] + public bool ConsentContinuation { get; set; } - /// Whether the host supplies bearer tokens dynamically. - [JsonPropertyName("hasBearerTokenProvider")] - public bool? HasBearerTokenProvider { get; set; } + /// Maximum accepted wall-clock deadline in milliseconds for one continuation call. + [JsonPropertyName("maxDeadlineMs")] + public long MaxDeadlineMs { get; set; } - /// Additional HTTP headers included with provider requests. - [JsonPropertyName("headers")] - public IDictionary? Headers { get; set; } + /// Maximum accepted polling attempts for one continuation call. + [JsonPropertyName("maxPollAttempts")] + public long MaxPollAttempts { get; set; } - /// Unique provider name used to qualify model selection IDs. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Maximum accepted delay in milliseconds between polling attempts. + [JsonPropertyName("maxPollIntervalMs")] + public long MaxPollIntervalMs { get; set; } - /// Transport used to communicate with the provider. - [JsonPropertyName("transport")] - public ProviderConfigTransport? Transport { 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; } - /// Provider protocol family. - [JsonPropertyName("type")] - public ProviderConfigType? Type { get; set; } + /// Whether getAccount is supported. Absence means false. + [JsonPropertyName("sessionAccountSelection")] + public bool? SessionAccountSelection { get; set; } - /// Wire API used to communicate with the provider. - [JsonPropertyName("wireApi")] - public ProviderConfigWireApi? WireApi { get; set; } + /// Whether reconcile accepts forceConnectorName. Absence means false. + [JsonPropertyName("targetedReconcile")] + public bool? TargetedReconcile { 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. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ProviderAddRequest +internal sealed class SessionConnectorsGetCapabilitiesRequest { - /// 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. +/// Credential-free identity metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ProviderSyncRequest +public sealed class AuthIdentityMetadata { - /// BYOK model definition snapshot. Models absent from this list are removed. - [JsonPropertyName("models")] - public IList? Models { get; set; } + /// Identity host. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Named BYOK provider connection snapshot. Providers absent from this list are removed. - [JsonPropertyName("providers")] - public IList? Providers { get; set; } + /// User 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; } } -/// What the withdrawal actually removed from the registry. +/// Session account selection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderWithdrawResult +public sealed class ConnectorSessionAccount { - /// 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; } + /// Opaque session-scoped account selection ID. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; - /// Selection ids that were registered and are now withdrawn. Excludes requested ids that were not present. - [JsonPropertyName("withdrawn")] - public IList Withdrawn { get => field ??= []; set; } + /// Credential-free identity metadata. + [JsonPropertyName("authInfo")] + public AuthIdentityMetadata AuthInfo { get => field ??= new(); set; } } -/// Host-managed model selection ids to withdraw from the session's BYOK registry. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ProviderWithdrawRequest +internal sealed class SessionConnectorsGetAccountRequest { - /// 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. +/// 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 SessionUpdateOptionsResult +public sealed class ConnectorAuthorizationRequirement { - /// Number of hooks loaded from installed plugins, returned when installedPlugins is updated. - [JsonPropertyName("pluginHookCount")] - public long? PluginHookCount { get; set; } + /// Exact opaque account selection that made the Connector request. + [JsonPropertyName("accountId")] + public string AccountId { get; set; } = string.Empty; - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Stable OAuth scope the selected account must grant. + [JsonPropertyName("scope")] + public ConnectorAuthorizationScope Scope { get; set; } } -/// Source descriptor for a `session.options.update` content-exclusion rule, with source name and type. +/// Credential-free Connector catalog entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OptionsUpdateAdditionalContentExclusionPolicyRuleSource +public sealed class ConnectorCatalogEntry { - /// Name of the policy source. + /// 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; - /// Type of the policy source. - [JsonPropertyName("type")] - public string Type { 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; } } -/// Single content-exclusion rule supplied to `session.options.update`, with paths, match conditions, and source. +/// Validated Connector catalog snapshot cached by the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OptionsUpdateAdditionalContentExclusionPolicyRule +public sealed class ConnectorCatalogResult { - /// Conditions of which at least one must match. - [JsonPropertyName("ifAnyMatch")] - public IList? IfAnyMatch { get; set; } + /// Validated catalog entries in service order. + [JsonPropertyName("connectors")] + public IList Connectors { get => field ??= []; 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; } + /// Unix epoch milliseconds when this snapshot was accepted. + [JsonPropertyName("refreshedAtMs")] + public long RefreshedAtMs { get; 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; } + /// Monotonically increasing session-local catalog revision. + [JsonPropertyName("revision")] + public long Revision { get; set; } } -/// Content-exclusion policy supplied to `session.options.update`, with rules, last-updated data, and scope. +/// Live status of one session-owned MCP projection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class OptionsUpdateAdditionalContentExclusionPolicy +public sealed class ConnectorRuntimeStatus { - /// Opaque policy update timestamp supplied by the host. - [JsonPropertyName("last_updated_at")] - public JsonElement LastUpdatedAt { get; set; } + /// Canonical Connector name that owns this server. + [JsonPropertyName("connectorName")] + public string ConnectorName { get; set; } = string.Empty; - /// Content-exclusion rules to apply. - [JsonPropertyName("rules")] - public IList Rules { get => field ??= []; set; } + /// Opaque runtime server ID. + [JsonPropertyName("runtimeServerId")] + public string RuntimeServerId { get; set; } = string.Empty; - /// Allowed values for the `OptionsUpdateAdditionalContentExclusionPolicyScope` enumeration. - [JsonPropertyName("scope")] - public OptionsUpdateAdditionalContentExclusionPolicyScope Scope { get; set; } + /// Current live MCP host status. + [JsonPropertyName("status")] + public ConnectorMcpStatus Status { get; set; } } -/// Options scoped to the built-in CAPI (Copilot API) provider. +/// Authoritative session connector state. Account IDs are opaque routing identifiers and credentials are never included. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CapiSessionOptions +public sealed class ConnectorStatus { - /// 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; } -} + /// Opaque account selection pinned to this session, when one has been selected. + [JsonPropertyName("accountId")] + public string? AccountId { 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; } + /// Connector API contract version. + [JsonPropertyName("apiVersion")] + public long ApiVersion { get; set; } - /// Whether the plugin is currently enabled. - [JsonPropertyName("enabled")] - public bool Enabled { 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; } - /// Installation timestamp (ISO-8601). - [JsonPropertyName("installed_at")] - public string InstalledAt { get; set; } = string.Empty; + /// Current feature and session availability. + [JsonPropertyName("availability")] + public ConnectorAvailability Availability { 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; } + /// Latest validated catalog snapshot, when available. + [JsonPropertyName("catalog")] + public ConnectorCatalogResult? Catalog { get; set; } - /// Marketplace the plugin came from (empty string for direct repo installs). - [JsonPropertyName("marketplace")] - public string Marketplace { get; set; } = string.Empty; + /// Number of active opaque connection continuations. + [JsonPropertyName("pendingConnections")] + public long PendingConnections { get; set; } - /// Plugin name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Live MCP status for every Connector-owned runtime server. + [JsonPropertyName("runtimeServers")] + public IList RuntimeServers { get => field ??= []; set; } +} - /// Source descriptor for direct repo installs (when marketplace is empty). - [JsonPropertyName("source")] - public JsonElement? Source { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionConnectorsGetStatusRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// 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; } +/// 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; - /// Installed version, if known. - [JsonPropertyName("version")] - public string? Version { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Custom model-provider configuration (BYOK). +/// Typed result of initiating or continuing a Connector connection. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProviderConfig +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(ConnectorConnectResultConnected), "connected")] +[JsonDerivedType(typeof(ConnectorConnectResultConsentRequired), "consent_required")] +[JsonDerivedType(typeof(ConnectorConnectResultPending), "pending")] +public partial class ConnectorConnectResult { - /// API key. Optional for local providers like Ollama. - [JsonPropertyName("apiKey")] - public string? ApiKey { get; set; } + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// Azure-specific provider options. - [JsonPropertyName("azure")] - public ProviderConfigAzure? Azure { get; set; } - /// API endpoint URL. - [JsonPropertyName("baseUrl")] - public string BaseUrl { 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"; - /// Bearer token for authentication. Sets the Authorization header directly. Takes precedence over apiKey when both are set. - [JsonPropertyName("bearerToken")] - public string? BearerToken { get; set; } + /// Fresh authoritative Connector state after MCP reconciliation. + [JsonPropertyName("status")] + public required ConnectorStatus Status { 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; } +/// 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"; - /// Custom HTTP headers to include in all outbound requests to the provider. - [JsonPropertyName("headers")] - public IDictionary? Headers { get; set; } + /// Validated HTTPS consent URL. The runtime does not open it. + [JsonPropertyName("consentUrl")] + public required string ConsentUrl { get; set; } - /// Maximum context window tokens for the model. - [JsonPropertyName("maxContextWindowTokens")] - public double? MaxContextWindowTokens { get; set; } + /// Opaque ID accepted by continueConnection. + [JsonPropertyName("continuationId")] + public required string ContinuationId { get; set; } +} - /// Maximum output tokens for the model. - [JsonPropertyName("maxOutputTokens")] - public double? MaxOutputTokens { 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"; - /// Maximum prompt/input tokens for the model. - [JsonPropertyName("maxPromptTokens")] - public double? MaxPromptTokens { get; set; } + /// Opaque ID accepted by continueConnection. + [JsonPropertyName("continuationId")] + public required string ContinuationId { get; set; } +} - /// Overrides for model capabilities when they cannot be inferred from modelId. - [JsonPropertyName("modelCapabilities")] - public ModelCapabilitiesOverride? ModelCapabilities { 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; - /// 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; } + /// Canonical Connector name from the current catalog. + [JsonPropertyName("connectorName")] + public string ConnectorName { get; set; } = string.Empty; - /// Provider name used for model and telemetry attribution. - [JsonPropertyName("providerName")] - public string? ProviderName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Provider transport. Defaults to "http". - [JsonPropertyName("transport")] - public ProviderConfigTransport? Transport { get; set; } +/// 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; - /// Provider type. Defaults to "openai" for generic OpenAI-compatible APIs. - [JsonPropertyName("type")] - public ProviderConfigType? Type { 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; } - /// Wire API format (openai/azure only). Defaults to "completions". - [JsonPropertyName("wireApi")] - public ProviderConfigWireApi? WireApi { get; set; } + /// Maximum catalog requests made by this call. Must be between one and the capability limit. + [JsonPropertyName("maxAttempts")] + public int MaxAttempts { 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; } + /// 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; } -/// 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. +/// Authoritative result after disconnect and MCP reconciliation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigAuth +public sealed class ConnectorDisconnectResult { - /// 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 the service accepted the idempotent disconnect. + [JsonPropertyName("disconnected")] + public bool Disconnected { 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; } + /// Fresh authoritative session state after removing Connector-owned MCP servers. + [JsonPropertyName("status")] + public ConnectorStatus Status { get => field ??= new(); set; } } -/// Destinations authorized to receive one masked environment credential. +/// Requests authoritative Connector-to-MCP reconciliation for the pinned account. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxMaskedEnvVar +public sealed class ConnectorReconcileRequest { - /// 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; } + /// 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; } } -/// Whole-value environment credential masking for sandboxed children. +/// Requests authoritative Connector-to-MCP reconciliation for the pinned account. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxCredentialsConfig +internal sealed class ConnectorReconcileRequestWithSession { - /// 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; } + /// 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; } -/// macOS seatbelt experimental options. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyExperimentalSeatbelt +internal sealed class SessionConnectorsWithdrawProjectionRequest { - /// Whether the macOS seatbelt profile may access the keychain. - [JsonPropertyName("keychainAccess")] - public bool? KeychainAccess { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Platform-specific experimental policy fields. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyExperimental +internal sealed class SessionManagedSettingsGetRequest { - /// macOS seatbelt experimental options. - [JsonPropertyName("seatbelt")] - public SandboxConfigUserPolicyExperimentalSeatbelt? Seatbelt { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Filesystem rules to merge into the base policy. +/// Session plugin metadata, with name, marketplace, optional version, and enabled state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyFilesystem +public sealed class Plugin { - /// Whether to clear the policy when the session exits. - [JsonPropertyName("clearPolicyOnExit")] - public bool? ClearPolicyOnExit { get; set; } + /// Opaque stable identity for a direct plugin source. + [JsonPropertyName("directSourceId")] + public string? DirectSourceId { get; set; } - /// Paths explicitly denied. - [JsonPropertyName("deniedPaths")] - public IList? DeniedPaths { get; set; } + /// Whether the plugin is currently enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } - /// Paths granted read-only access. - [JsonPropertyName("readonlyPaths")] - public IList? ReadonlyPaths { get; set; } + /// Whether this managed desired plugin has an installed or live record. + [JsonPropertyName("installed")] + public bool? Installed { get; set; } - /// Paths granted read/write access. - [JsonPropertyName("readwritePaths")] - public IList? ReadwritePaths { 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; } } -/// HTTP proxy configuration for sandboxed traffic. +/// Plugins installed for the session, with their enabled state and version metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyNetworkProxy +public sealed class PluginList { - /// 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; + /// Installed plugins. + [JsonPropertyName("plugins")] + public IList Plugins { get => field ??= []; set; } +} - /// 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; } +/// 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; } -/// Network rules to merge into the base policy. +/// Plugin source resolved relative to the session's authoritative working directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicyNetwork +internal sealed class SessionPluginsInstallRequest { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Whether traffic to local/loopback addresses is allowed. - [JsonPropertyName("allowLocalNetwork")] - public bool? AllowLocalNetwork { get; set; } + /// 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; +} - /// Whether outbound network traffic is allowed at all. - [JsonPropertyName("allowOutbound")] - public bool? AllowOutbound { get; set; } +/// 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; } - /// 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; } + /// Plugin name or "plugin@marketplace" spec to uninstall. When ambiguous, prefer the fully-qualified spec. + [JsonPropertyName("name")] + public string Name { 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// macOS seatbelt-specific options. +/// Name (or spec) of the plugin to update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicySeatbelt +internal sealed class PluginsUpdateRequestWithSession { - /// Whether the macOS seatbelt profile may access the keychain. - [JsonPropertyName("keychainAccess")] - public bool? KeychainAccess { get; set; } + /// 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; } -/// User-managed sandbox policy fragment merged into the auto-discovered base policy. +/// Plugin names (or specs) to enable in the session's authoritative working directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfigUserPolicy +internal sealed class SessionPluginsEnableRequest { - /// Deprecated legacy location for `seatbelt`; read only when the top-level `seatbelt` is absent. - [JsonPropertyName("experimental")] - public SandboxConfigUserPolicyExperimental? Experimental { get; set; } + /// Plugin names or "plugin@marketplace" specs to enable. Unknown names are ignored. Non-marketplace direct installs are always enabled and cannot be toggled via this API. + [JsonPropertyName("names")] + public IList Names { get => field ??= []; set; } - /// Filesystem rules to merge into the base policy. - [JsonPropertyName("filesystem")] - public SandboxConfigUserPolicyFilesystem? Filesystem { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Network rules to merge into the base policy. - [JsonPropertyName("network")] - public SandboxConfigUserPolicyNetwork? Network { get; set; } +/// 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; } - /// macOS seatbelt options to merge into the base policy. - [JsonPropertyName("seatbelt")] - public SandboxConfigUserPolicySeatbelt? Seatbelt { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Resolved sandbox configuration. +/// RPC data type for SessionPluginsReload operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SandboxConfig +public sealed class SessionPluginsReloadRequest { - /// Whether to auto-add the current working directory to readwritePaths. Default: true. - [JsonPropertyName("addCurrentWorkingDirectory")] - public bool? AddCurrentWorkingDirectory { get; set; } + /// 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; } - /// 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; } + /// Re-run custom-agent discovery after refreshing plugins. Defaults to true. + [JsonPropertyName("reloadCustomAgents")] + public bool? ReloadCustomAgents { 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; } + /// 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; } - /// Credential-injection capability flags. - [JsonPropertyName("auth")] - public SandboxConfigAuth? Auth { 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; } - /// 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; } + /// Reload MCP server connections after refreshing plugins. Defaults to true. + [JsonPropertyName("reloadMcp")] + public bool? ReloadMcp { get; set; } +} - /// Whether sandboxing is enabled for the session. - [JsonPropertyName("enabled")] - public bool Enabled { 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; } - /// The `sandboxLspServers` counterpart of `managedMcpRoutingLocked`. - [JsonInclude] - [JsonPropertyName("managedLspRoutingLocked")] - internal bool? ManagedLspRoutingLocked { get; set; } + /// Re-run custom-agent discovery after refreshing plugins. Defaults to true. + [JsonPropertyName("reloadCustomAgents")] + public bool? ReloadCustomAgents { 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; } + /// 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; } - /// 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; } + /// 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; } - /// 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; } + /// Reload MCP server connections after refreshing plugins. Defaults to true. + [JsonPropertyName("reloadMcp")] + public bool? ReloadMcp { get; set; } - /// User-managed sandbox policy fragment merged into the auto-discovered base policy. - [JsonPropertyName("userPolicy")] - public SandboxConfigUserPolicy? UserPolicy { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// -/// 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. -/// +/// Retry outcome for one plugin required by managed settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellCredentials +public sealed class ManagedPluginRetryEntry { - /// - /// 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; } + /// Why the plugin was not prepared. Present when `status` is `failed` or `deferred`. + [JsonPropertyName("error")] + public string? Error { 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; } + /// 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; } } -/// A host-provided script sourced before each built-in shell command when its shell target matches the active shell. +/// Per-plugin outcomes of retrying the plugins required by the session's managed settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellInitScript +public sealed class SessionPluginsRetryManagedResult { - /// 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; } + /// 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; } } -/// Per-session settings for built-in shell tools. +/// RPC data type for SessionPluginsRetryManaged operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellOptions +public sealed class SessionPluginsRetryManagedRequest { - /// Command-scoped GitHub credential injection for shell commands. - [JsonPropertyName("credentials")] - public ShellCredentials? Credentials { get; set; } + /// "plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed settings require. + [JsonPropertyName("plugins")] + public IList? Plugins { get; set; } +} - /// Controls automatic non-interactive profile loading where supported. Explicit initScripts are unaffected. - [JsonPropertyName("initProfile")] - public ShellInitProfile? InitProfile { 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; } - /// - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// - /// 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; } +/// 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; } -/// Patch of mutable session options to apply to the running session. +/// Marketplace source and optional working directory for relative-path resolution. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionUpdateOptionsParams +internal sealed class PluginsMarketplacesAddRequestWithSession { - /// 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Runtime context discriminator (e.g., `cli`, `actions`). - [JsonPropertyName("agentContext")] - public string? AgentContext { 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; - /// Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers. - [JsonPropertyName("allowAllMcpServerInstructions")] - public bool? AllowAllMcpServerInstructions { 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; } +} - /// Whether to disable the `ask_user` tool (encourages autonomous behavior). - [JsonPropertyName("askUserDisabled")] - public bool? AskUserDisabled { 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; } - /// Allowlist of tool names available to this session. - [JsonPropertyName("availableTools")] - public IList? AvailableTools { get; set; } + /// Marketplace name to remove. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Options scoped to the built-in CAPI (Copilot API) provider. - [JsonPropertyName("capi")] - public CapiSessionOptions? Capi { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Identifier of the client driving the session. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } +/// Name of the marketplace whose plugin catalog to fetch. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PluginsMarketplacesBrowseRequestWithSession +{ + /// Marketplace name to browse. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Whether to include the `Co-authored-by` trailer in commit messages. - [JsonPropertyName("coauthorEnabled")] - public bool? CoauthorEnabled { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Context tier for models with tiered pricing. The session uses this to derive effective `modelCapabilitiesOverrides` so compaction, truncation, token display, and request limits honor the selected tier. - [JsonPropertyName("contextTier")] - public OptionsUpdateContextTier? ContextTier { get; set; } +/// 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; } +} - /// Whether to allow auto-mode continuation across turns. - [JsonPropertyName("continueOnAutoMode")] - public bool? ContinueOnAutoMode { 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; } - /// Override URL for the Copilot API endpoint. - [JsonPropertyName("copilotUrl")] - public string? CopilotUrl { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Whether to default custom agents to local-only execution. - [JsonPropertyName("customAgentsLocalOnly")] - public bool? CustomAgentsLocalOnly { get; set; } +/// Short-lived, rotating credential the caller must send on every request, in addition to `apiKey` if one is present. Omitted when the endpoint does not require one. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderSessionToken +{ + /// When the token expires, if known. Callers should refresh by calling `getEndpoint` again before this time, or reactively on any 401/403 response from `baseUrl`. + [JsonPropertyName("expiresAt")] + public DateTimeOffset? ExpiresAt { get; set; } - /// Instruction source IDs to exclude from the system prompt. - [JsonPropertyName("disabledInstructionSources")] - public IList? DisabledInstructionSources { get; set; } + /// HTTP header name the token must be sent under. + [JsonPropertyName("header")] + public string Header { get; set; } = string.Empty; - /// Skill IDs that should be excluded from this session. - [JsonPropertyName("disabledSkills")] - public IList? DisabledSkills { get; set; } + /// The model the token is bound to, when applicable. When set, the token is only valid for requests against this model. + [JsonPropertyName("model")] + public string? Model { get; set; } - /// Whether to enable loading of `.github/hooks/` filesystem hooks. Separate from the SDK callback hook mechanism. - [JsonPropertyName("enableFileHooks")] - public bool? EnableFileHooks { get; set; } + /// The short-lived token value. + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; +} - /// Whether to enable host git operations (context resolution, child repo scanning, git info in system prompt). - [JsonPropertyName("enableHostGitOperations")] - public bool? EnableHostGitOperations { get; set; } +/// 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; } - /// 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; } + /// Base URL to pass to the LLM client library. + [Url] + [StringSyntax(StringSyntaxAttribute.Uri)] + [JsonPropertyName("baseUrl")] + public string BaseUrl { get; set; } = string.Empty; - /// Whether to surface reasoning-summary events from the model. - [JsonPropertyName("enableReasoningSummaries")] - public bool? EnableReasoningSummaries { get; set; } + /// HTTP headers the caller must include on every outbound request. + [JsonPropertyName("headers")] + public IDictionary Headers { get => field ??= new Dictionary(); set; } - /// Whether shell-script safety heuristics are enabled. - [JsonPropertyName("enableScriptSafety")] - public bool? EnableScriptSafety { get; set; } + /// Short-lived, rotating credential the caller must send on every request, in addition to `apiKey` if one is present. Omitted when the endpoint does not require one. + [JsonPropertyName("sessionToken")] + public ProviderSessionToken? SessionToken { get; set; } - /// Whether to enable cross-session store writes and reads. - [JsonPropertyName("enableSessionStore")] - public bool? EnableSessionStore { get; set; } + /// Transport to be used for provider requests. + [JsonPropertyName("transport")] + public ProviderEndpointTransport? Transport { 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; } + /// Provider family. Matches the `type` field of a BYOK provider config. + [JsonPropertyName("type")] + public ProviderEndpointType Type { get; set; } - /// Whether to stream model responses. - [JsonPropertyName("enableStreaming")] - public bool? EnableStreaming { get; set; } + /// Wire API to be used, when required for the provider type. + [JsonPropertyName("wireApi")] + public ProviderEndpointWireApi? WireApi { 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; } +/// 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; } +} - /// 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; } +/// 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; } - /// Whether subagent callback events should be forwarded into the session event log sink. - [JsonPropertyName("eventsLogIncludesSubagents")] - public bool? EventsLogIncludesSubagents { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// 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; } +/// 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; } +} - /// Denylist of tool names for this session. - [JsonPropertyName("excludedTools")] - public IList? ExcludedTools { 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. +[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; } - /// Map of feature-flag IDs to their boolean enabled state. - [JsonPropertyName("featureFlags")] - public IDictionary? FeatureFlags { 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; } - /// Skill scan directories and descendants excluded from discovery. Supports `~`-relative paths. - [JsonPropertyName("ignoredSkillsLocations")] - public IList? IgnoredSkillsLocations { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// 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; } +/// 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; } - /// 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; } + /// Synthesized selectable model entries for the synchronized BYOK models. + [JsonPropertyName("models")] + public IList Models { get => field ??= []; 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; } + /// Provider-qualified model selection ids present after synchronization. + [JsonPropertyName("selectionIds")] + public IList SelectionIds { get => field ??= []; set; } +} - /// Stable integration identifier used for analytics and rate-limit attribution. - [JsonPropertyName("integrationId")] - public string? IntegrationId { get; 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; } - /// Whether experimental capabilities are enabled. - [JsonPropertyName("isExperimentalMode")] - public bool? IsExperimentalMode { get; set; } + /// Named BYOK provider connection snapshot. Providers absent from this list are removed. + [JsonPropertyName("providers")] + public IList? Providers { get; set; } - /// Whether interactive shell sessions are logged. - [JsonPropertyName("logInteractiveShells")] - public bool? LogInteractiveShells { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Identifier sent to LSP-style integrations. - [JsonPropertyName("lspClientName")] - public string? LspClientName { get; set; } +/// 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; } - /// 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; } + /// 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; } - /// 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; } + /// Selection ids that were registered and are now withdrawn. Excludes requested ids that were not present. + [JsonPropertyName("withdrawn")] + public IList Withdrawn { get => field ??= []; set; } +} - /// The model ID to use for assistant turns. - [JsonPropertyName("model")] - public string? Model { get; 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; } - /// Per-property model capability overrides for the selected model. - [JsonPropertyName("modelCapabilitiesOverrides")] - public ModelCapabilitiesOverride? ModelCapabilitiesOverrides { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Organization-level custom instructions to inject into the system prompt. - [JsonPropertyName("organizationCustomInstructions")] - public string? OrganizationCustomInstructions { get; set; } +/// 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; } - /// Custom model-provider configuration (BYOK). - [JsonPropertyName("provider")] - public ProviderConfig? Provider { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { 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; } +/// 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; - /// Reasoning summary mode for supported model clients. - [JsonPropertyName("reasoningSummary")] - public OptionsUpdateReasoningSummary? ReasoningSummary { get; set; } + /// Type of the policy source. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; +} + +/// Single content-exclusion rule supplied to `session.options.update`, with paths, match conditions, and source. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class OptionsUpdateAdditionalContentExclusionPolicyRule +{ + /// Conditions of which at least one must match. + [JsonPropertyName("ifAnyMatch")] + public IList? IfAnyMatch { get; set; } + + /// Conditions none of which may match. + [JsonPropertyName("ifNoneMatch")] + public IList? IfNoneMatch { get; set; } + + /// Path patterns covered by this rule. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } + + /// Source descriptor for a `session.options.update` content-exclusion rule, with source name and type. + [JsonPropertyName("source")] + public OptionsUpdateAdditionalContentExclusionPolicyRuleSource Source { get => field ??= new(); set; } +} + +/// Content-exclusion policy supplied to `session.options.update`, with rules, last-updated data, and scope. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class OptionsUpdateAdditionalContentExclusionPolicy +{ + /// Opaque policy update timestamp supplied by the host. + [JsonPropertyName("last_updated_at")] + public JsonElement LastUpdatedAt { get; set; } + + /// Content-exclusion rules to apply. + [JsonPropertyName("rules")] + public IList Rules { get => field ??= []; set; } + + /// Allowed values for the `OptionsUpdateAdditionalContentExclusionPolicyScope` enumeration. + [JsonPropertyName("scope")] + public OptionsUpdateAdditionalContentExclusionPolicyScope Scope { get; set; } +} + +/// Options scoped to the built-in CAPI (Copilot API) provider. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CapiSessionOptions +{ + /// Routing preference for sessions whose model is `auto`. On create or cold resume, this establishes the preference sent as `tier` on CAPI `/auto` requests; when omitted on cold resume, the runtime restores the last committed preference. On resident resume, a different value requests a safe switch after resume succeeds and cannot change an in-flight turn. Successful switches are persisted for later cold resume. When no preference is supplied or restored, CAPI default routing is used. `fast` is an integrator-only latency preset, not a first-party GitHub Copilot product preference. + [JsonPropertyName("autoTier")] + public AutoTier? AutoTier { get; set; } + + /// Whether to use WebSocket transport for the CAPI Responses API. Enabled by default when the model advertises `ws:/responses` support; set to `false` to force the HTTP Responses transport in environments where WebSockets are blocked (e.g. behind a proxy). Setting this to `false` is equivalent to the `COPILOT_CLI_DISABLE_WEBSOCKET_RESPONSES` environment variable. + [JsonPropertyName("enableWebSocketResponses")] + public bool? EnableWebSocketResponses { get; set; } +} + +/// Installed plugin record for a session, with marketplace, version, install time, enabled state, cache path, and source. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionInstalledPlugin +{ + /// Path where the plugin is cached locally. + [JsonPropertyName("cache_path")] + public string? CachePath { get; set; } + + /// Whether the plugin is currently enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } + + /// Installation timestamp (ISO-8601). + [JsonPropertyName("installed_at")] + public string InstalledAt { get; set; } = string.Empty; + + /// Absolute path of the marketplace directory a live plugin was resolved from. Present only on live, never-persisted records — those synthesized at session start for a directory/local marketplace, whose cache_path points at the real plugin directory on disk rather than a copy under the installed-plugins cache. Its presence is what marks a record as live, and no record carrying it is ever written to the persisted installedPlugins key. + [JsonPropertyName("installed_from")] + public string? InstalledFrom { get; set; } + + /// Marketplace the plugin came from (empty string for direct repo installs). + [JsonPropertyName("marketplace")] + public string Marketplace { get; set; } = string.Empty; + + /// Plugin name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + /// Source descriptor for direct repo installs (when marketplace is empty). + [JsonPropertyName("source")] + public JsonElement? Source { get; set; } + + /// Per-plugin source fingerprint (a SHA-256 hash of the plugin's catalog source spec plus its resolved source subtree — NOT a Git commit SHA) captured at marketplace install/update time. Auto-update compares it against the freshly recomputed fingerprint to detect a content change that does not bump the version. Absent for pre-existing installs and for direct (non-marketplace) installs. + [JsonPropertyName("source_sha")] + public string? SourceSha { get; set; } + + /// Installed version, if known. + [JsonPropertyName("version")] + public string? Version { get; set; } +} + +/// Custom model-provider configuration (BYOK). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProviderConfig +{ + /// API key. Optional for local providers like Ollama. + [JsonPropertyName("apiKey")] + public string? ApiKey { get; set; } + + /// Azure-specific provider options. + [JsonPropertyName("azure")] + public ProviderConfigAzure? Azure { get; set; } + + /// API endpoint URL. + [JsonPropertyName("baseUrl")] + public string BaseUrl { get; set; } = string.Empty; + + /// Bearer token for authentication. Sets the Authorization header directly. Takes precedence over apiKey when both are set. + [JsonPropertyName("bearerToken")] + public string? BearerToken { get; set; } + + /// When true, the SDK client supplies bearer tokens on demand: the runtime calls the client-session `providerToken.getToken` callback before each request and applies the returned token as an `Authorization: Bearer <token>` header. This is the bearer/OAuth scheme used by Azure AD / managed-identity tokens and provider OAuth access tokens (including Anthropic's), not a provider-specific API-key header such as Anthropic's `x-api-key`. The token-acquiring function itself stays on the SDK side and is never serialized; only this flag crosses the wire. When set alongside `apiKey`/`bearerToken`, the callback takes precedence: the runtime applies the token returned by `providerToken.getToken` as the `Authorization: Bearer` header for each request and does not send the static credential. + [JsonPropertyName("hasBearerTokenProvider")] + public bool? HasBearerTokenProvider { get; set; } + + /// Custom HTTP headers to include in all outbound requests to the provider. + [JsonPropertyName("headers")] + public IDictionary? Headers { get; set; } + + /// Maximum context window tokens for the model. + [JsonPropertyName("maxContextWindowTokens")] + public double? MaxContextWindowTokens { get; set; } + + /// Maximum output tokens for the model. + [JsonPropertyName("maxOutputTokens")] + public double? MaxOutputTokens { get; set; } + + /// Maximum prompt/input tokens for the model. + [JsonPropertyName("maxPromptTokens")] + public double? MaxPromptTokens { get; set; } + + /// Overrides for model capabilities when they cannot be inferred from modelId. + [JsonPropertyName("modelCapabilities")] + public ModelCapabilitiesOverride? ModelCapabilities { get; set; } + + /// Well-known model ID used for capability lookup. When set, agent behavior config and token limits are inferred from this model. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } + + /// The product serving the model, reported in telemetry as `model_provider`. Set it when `type` alone cannot identify the product, such as Ollama or LM Studio behind an OpenAI-compatible endpoint. Only affects telemetry. + [JsonPropertyName("modelProvider")] + public ProviderConfigModelProvider? ModelProvider { get; set; } + + /// Provider name used for model and telemetry attribution. + [JsonPropertyName("providerName")] + public string? ProviderName { get; set; } + + /// Provider transport. Defaults to "http". + [JsonPropertyName("transport")] + public ProviderConfigTransport? Transport { get; set; } + + /// Provider type. Defaults to "openai" for generic OpenAI-compatible APIs. + [JsonPropertyName("type")] + public ProviderConfigType? Type { get; set; } + + /// Wire API format (openai/azure only). Defaults to "completions". + [JsonPropertyName("wireApi")] + public ProviderConfigWireApi? WireApi { get; set; } + + /// The model identifier sent to the provider API for inference (the "wire" model), as opposed to modelId which is the well-known base. + [JsonPropertyName("wireModel")] + public string? WireModel { get; set; } +} + +/// +/// Command-scoped GitHub credential injection for the shell commands an agent runs. +/// +/// Each channel is opt-in and independent, and injection is scoped to the individual command +/// spawn: the credential is resolved from the session's *current* authentication at every spawn +/// and reaches only spawns whose script actually invokes `git` or `gh`. Because nothing is +/// retained between spawns, replacing the session credential (`session.gitHubAuth.setCredentials`) +/// changes what the next spawned command presents — which seeding a credential into the runtime +/// process's own environment cannot do, since a child's environment is fixed at `exec`. +/// +/// The credential is matched to the host it authenticates to, so a github.com credential is never +/// presented to a GitHub Enterprise host and vice versa. Where a channel cannot express that +/// boundary it injects nothing rather than crossing it -- see `gh` below. +/// +/// This is independent of `sandboxConfig`: it is a decision about which identity the agent +/// presents, not about what the agent may touch, and it works on every platform whether or not +/// an OS sandboxing backend is available. `sandboxConfig.auth` remains the sandbox-scoped +/// spelling and is additive with this one. +/// +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ShellCredentials +{ + /// + /// Whether to authenticate the agent's `gh` commands as the session's GitHub credential, by + /// exporting `GH_TOKEN` to a spawn that runs `gh`. Any inherited `gh` credential is removed from + /// spawns that do not, so the credential stays command-scoped. + /// + /// Applies to a github.com credential only. `gh` picks its credential variable from the host a + /// command targets rather than the one the credential belongs to, and the command can choose that + /// target, so `GH_ENTERPRISE_TOKEN` would offer a single-tenant enterprise credential to every + /// other enterprise host. A session whose credential is enterprise-scoped therefore runs `gh` + /// unauthenticated; its `git` commands are unaffected, because `http.<host>.extraheader` is scoped + /// to one host by construction. Default: false (opt-in). + /// + [JsonPropertyName("gh")] + public bool? Gh { get; set; } + + /// + /// Whether to authenticate the agent's `git` commands as the session's GitHub credential, by + /// injecting an `http.<host>.extraheader` (plus `insteadOf` rewrites so SSH-spelled remotes for + /// that host use the authenticated HTTPS transport). Applied only to a spawn that runs a + /// remote-contacting `git` subcommand. Default: false (opt-in). + /// + [JsonPropertyName("git")] + public bool? Git { get; set; } +} + +/// A host-provided script sourced before each built-in shell command when its shell target matches the active shell. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ShellInitScript +{ + /// Path to the script to source. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Built-in shell that may source this script. + [JsonPropertyName("shell")] + public ShellInitScriptShell Shell { get; set; } +} + +/// Per-session settings for built-in shell tools. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ShellOptions +{ + /// Command-scoped GitHub credential injection for shell commands. + [JsonPropertyName("credentials")] + public ShellCredentials? Credentials { get; set; } + + /// Controls automatic non-interactive profile loading where supported. Explicit initScripts are unaffected. + [JsonPropertyName("initProfile")] + public ShellInitProfile? InitProfile { get; set; } + + /// + /// Ordered host-provided script paths sourced before each built-in shell command when the + /// entry's shell target matches the active shell. Use these for rc files, environment setup scripts, + /// or other custom scripts. A script that returns a nonzero status is reported, and later scripts + /// and the user command continue while the shell remains running. Because scripts are sourced into + /// the command shell, `exit`, `exec`, failures under `set -e`, or other shell-terminating behavior + /// can prevent continuation. Script standard output is preserved; Bash script stderr is discarded, + /// PowerShell exception messages are replaced, and runtime-generated failure notices omit + /// configured script paths. When sandboxing is enabled, each script must already be readable under + /// the active sandbox filesystem policy. Pass an empty array to clear the list. + /// + [JsonPropertyName("initScripts")] + public IList? InitScripts { get; set; } + + /// + /// Flags passed to the active built-in shell process on startup, replacing its default flags. + /// When omitted, the built-in Bash shell uses `--norc --noprofile`, + /// and the built-in PowerShell shell uses `-NoProfile -NoLogo`. + /// + [JsonPropertyName("processFlags")] + public IList? ProcessFlags { get; set; } +} + +/// Patch of mutable session options to apply to the running session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionUpdateOptionsParams +{ + /// Additional content-exclusion policies to merge into the session's policy set. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + [JsonPropertyName("additionalContentExclusionPolicies")] + public IList? AdditionalContentExclusionPolicies { get; set; } + + /// Runtime context discriminator (e.g., `cli`, `actions`). + [JsonPropertyName("agentContext")] + public string? AgentContext { get; set; } + + /// 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 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; } + + /// 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 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; } + + /// 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; } + + /// 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; } + + /// 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")] @@ -18243,1515 +20123,2825 @@ internal sealed class SessionUpdateOptionsParams [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")] -#endif - [JsonPropertyName("shellInitProfile")] - public string? ShellInitProfile { 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; } + + /// Additional directories to search for skills. + [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 partial class ExternalToolTextResultForLlmContentImage : ExternalToolTextResultForLlmContent +{ + /// + [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; } +} + +/// 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"; + + /// 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; } +} + +/// 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"; + + /// 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; } + + /// 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; } +} + +/// 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; } - /// PowerShell process flags applied to built-in and user-requested shell commands. - [JsonPropertyName("shellProcessFlags")] - public IList? ShellProcessFlags { get; set; } + /// Completion-review decision produced by the task-completion tool. + [JsonPropertyName("taskCompletionDecision")] + public TaskCompletionDecision? TaskCompletionDecision { get; set; } - /// Additional directories to search for skills. - [JsonPropertyName("skillDirectories")] - public IList? SkillDirectories { get; set; } + /// Text result returned to the model. + [JsonPropertyName("textResultForLlm")] + public string TextResultForLlm { get; set; } = string.Empty; - /// Whether to skip loading custom instruction sources. - [JsonPropertyName("skipCustomInstructions")] - public bool? SkipCustomInstructions { get; set; } + /// Deferred tool names made available by this result. + [JsonPropertyName("toolReferences")] + public IList? ToolReferences { get; set; } - /// Whether to skip embedding retrieval pipeline initialization and execution. - [JsonPropertyName("skipEmbeddingRetrieval")] - public bool? SkipEmbeddingRetrieval { get; set; } + /// Tool-specific telemetry payload. + [JsonPropertyName("toolTelemetry")] + public JsonElement? ToolTelemetry { 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; } + /// Optional UI resource produced by the tool. + [JsonPropertyName("uiResource")] + public JsonElement? UiResource { get; set; } +} - /// Controls how availableTools (allowlist) and excludedTools (denylist) combine when both are set. - [JsonPropertyName("toolFilterPrecedence")] - public OptionsUpdateToolFilterPrecedence? ToolFilterPrecedence { 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; } - /// Optional path for trajectory output. - [JsonPropertyName("trajectoryFile")] - public string? TrajectoryFile { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Output verbosity level for supported models. - [JsonPropertyName("verbosity")] - public Verbosity? Verbosity { get; set; } + /// Arguments supplied to the completed task_complete tool call. + [JsonPropertyName("toolArgs")] + public JsonElement ToolArgs { get; set; } +} - /// Absolute working-directory path for shell tools. - [JsonPropertyName("workingDirectory")] - public string? WorkingDirectory { 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; } } -/// Parameters for (re)loading the merged LSP configuration 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 LspInitializeRequest +internal sealed class HandlePendingToolCallRequest { - /// Force re-initialization even when LSP configs were already loaded for the working directory. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// Error message if the tool call failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Git root used as the boundary when traversing for project-level LSP configs (supports monorepos). - [JsonPropertyName("gitRoot")] - public string? GitRoot { 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; - - /// 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. +/// 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 Extension +public sealed class PendingExternalToolRequest { - /// 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; + /// 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; } - /// Extension name (directory name). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Arguments to pass to the external tool. + [JsonPropertyName("arguments")] + public JsonElement? Arguments { get; set; } - /// Process ID if the extension is running. - [JsonPropertyName("pid")] - public long? Pid { 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; } - /// Discovery source: project (.github/extensions/), user (~/.copilot/extensions/), plugin (installed plugin), or session (session-state/<id>/extensions/). - [JsonPropertyName("source")] - public ExtensionSource Source { get; set; } + /// Request ID to pass to session.tools.handlePendingToolCall. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Current status: running, disabled, failed, or starting. - [JsonPropertyName("status")] - public ExtensionStatus Status { get; set; } + /// 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; } -/// Extensions discovered for the session, with their current status. +/// External tool calls still waiting for a result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExtensionList +public sealed class PendingExternalToolRequestList { - /// Discovered extensions and their current status. - [JsonPropertyName("extensions")] - public IList Extensions { get => field ??= []; set; } + /// External tool calls of the session and its sub-agents that session.tools.handlePendingToolCall would still accept, in the order they were requested. Includes calls that were pending when the session was last suspended and resumed with pending work continued, so a host that did not observe the original external_tool.requested event can answer them. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } } -/// Identifies the target session. +/// 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 SessionExtensionsListRequest +internal sealed class ToolsListPendingRequestsRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Source-qualified extension identifier to enable for the session. +/// 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)] -internal sealed class ExtensionsEnableRequest +public sealed class ToolsInitializeAndValidateResult { - /// Source-qualified extension ID to enable. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; +} +/// 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; } -/// Source-qualified extension identifier to disable for the session. +/// Lightweight metadata for a currently initialized session tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ExtensionsDisableRequest +public sealed class CurrentToolMetadata { - /// Source-qualified extension ID to disable. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Whether the tool is loaded on demand via tool search. + [JsonPropertyName("deferLoading")] + public bool? DeferLoading { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } } -/// Identifies the target session. +/// Current lightweight tool metadata snapshot for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionExtensionsReloadRequest +public sealed class ToolsGetCurrentMetadataResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Current tool metadata, or null when tools have not been initialized yet. + [JsonPropertyName("tools")] + public IList? Tools { get; set; } } -/// Attachment union accepted by push input, covering files, directories, GitHub objects, blobs, snippets, and extension context. -/// Polymorphic base type discriminated by type. +/// Identifies the target session. [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 +internal sealed class SessionToolsGetCurrentMetadataRequest { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } - -/// Optional line range to scope the attachment to a specific section of the file. +/// Empty result after replacing the calling connection's externally implemented tools. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentFileLineRange +public sealed class ToolsSetResult { - /// 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 . +/// Serializable definition of a caller-implemented tool whose execution is handled over the SDK connection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentFile : PushAttachment +public sealed class ProtocolExternalToolDefinition { - /// - [JsonIgnore] - public override string Type => "file"; + /// Tool-loading deferral policy. + [JsonPropertyName("defer")] + public ProtocolExternalToolDefer? Defer { get; set; } - /// User-facing display name for the attachment. - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } + /// Model-visible explanation of what the tool does. + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; - /// Optional line range to scope the attachment to a specific section of the file. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("lineRange")] - public PushAttachmentFileLineRange? LineRange { get; set; } + /// Whether the tool executes commands in a terminal. + [JsonPropertyName("isTerminal")] + public bool? IsTerminal { get; set; } - /// Absolute file path. - [JsonPropertyName("path")] - public required string Path { get; set; } -} + /// Optional caller-defined metadata associated with the tool. + [JsonPropertyName("metadata")] + public IDictionary? Metadata { 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"; + /// Unique model-visible tool name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// User-facing display name for the attachment. - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } + /// Whether this definition replaces a built-in tool with the same name. + [JsonPropertyName("overridesBuiltInTool")] + public bool? OverridesBuiltInTool { get; set; } - /// Absolute directory path. - [JsonPropertyName("path")] - public required string Path { get; set; } -} + /// JSON Schema describing the tool's input arguments. + [JsonPropertyName("parameters")] + public IDictionary? Parameters { 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; } + /// Whether execution bypasses the normal tool permission prompt. + [JsonPropertyName("skipPermission")] + public bool? SkipPermission { get; set; } - /// End line number (0-based). - [JsonPropertyName("line")] - public long Line { get; set; } + /// Optional human-readable display title. + [JsonPropertyName("title")] + public string? Title { get; set; } } -/// Start position of the selection. +/// 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 PushAttachmentSelectionDetailsStart +internal sealed class ToolsSetRequest { - /// Start character offset within the line (0-based). - [JsonPropertyName("character")] - public long Character { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Start line number (0-based). - [JsonPropertyName("line")] - public long Line { get; set; } + /// Complete replacement list for the calling connection. + [JsonPropertyName("tools")] + public IList Tools { get => field ??= []; set; } } -/// Position range of the selection within the file. +/// Empty result after applying subagent settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentSelectionDetails +public sealed class ToolsUpdateSubagentSettingsResult { - /// 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 . +/// Subagent model, reasoning effort, context tier, and auto-invocation settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentSelection : PushAttachment +public sealed class SubagentSettingsEntry { - /// - [JsonIgnore] - public override string Type => "selection"; + /// 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; } - /// User-facing display name for the selection. - [JsonPropertyName("displayName")] - public required string DisplayName { get; set; } + /// Context tier override for matching subagents. + [JsonPropertyName("contextTier")] + public SubagentSettingsEntryContextTier? ContextTier { get; set; } - /// Absolute path to the file containing the selection. - [JsonPropertyName("filePath")] - public required string FilePath { get; set; } + /// Reasoning effort override for matching subagents. + [JsonPropertyName("effortLevel")] + public string? EffortLevel { get; set; } - /// Position range of the selection within the file. - [JsonPropertyName("selection")] - public required PushAttachmentSelectionDetails Selection { get; set; } + /// Model override for matching subagents. + [JsonPropertyName("model")] + public string? Model { get; set; } - /// The selected text content. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// Whether the configured model strategy is preferred or required. + [JsonPropertyName("modelPolicy")] + public AgentModelPolicy? ModelPolicy { 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 +/// Configured per-agent subagent overrides. +public sealed class UpdateSubagentSettingsRequestSubagents { - /// - [JsonIgnore] - public override string Type => "github_reference"; + /// Per-agent settings keyed by subagent agent_type. + [JsonPropertyName("agents")] + public IDictionary? Agents { get; set; } - /// Issue, pull request, or discussion number. - [JsonPropertyName("number")] - public required long Number { get; set; } + /// Names of subagents the user has turned off; they cannot be dispatched. + [JsonPropertyName("disabledSubagents")] + public IList? DisabledSubagents { get; set; } - /// Type of GitHub reference. - [JsonPropertyName("referenceType")] - public required PushAttachmentGitHubReferenceType ReferenceType { get; set; } + /// Maximum number of subagents that can run concurrently; applies to usage-based billing users only. + [JsonPropertyName("maxConcurrency")] + public int? MaxConcurrency { get; set; } - /// Current state of the referenced item (e.g., open, closed, merged). - [JsonPropertyName("state")] - public required string State { get; set; } + /// Maximum subagent nesting depth; applies to usage-based billing users only. + [JsonPropertyName("maxDepth")] + public int? MaxDepth { get; set; } +} - /// Title of the referenced item. - [JsonPropertyName("title")] - public required string Title { 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; - /// URL to the referenced item on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Subagent settings to apply, or null to clear the live session override. + [JsonPropertyName("subagents")] + public UpdateSubagentSettingsRequestSubagents? Subagents { get; set; } } -/// Pointer to a GitHub repository. +/// RPC data type for SessionCommandsList operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushGitHubRepoRef +public sealed class SessionCommandsListRequest { - /// Numeric GitHub repository id. - [JsonPropertyName("id")] - public long? Id { get; set; } + /// Include runtime built-in commands. + [JsonPropertyName("includeBuiltins")] + public bool? IncludeBuiltins { get; set; } - /// Repository name (without owner). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Include commands registered by protocol clients, including SDK clients and extensions. + [JsonPropertyName("includeClientCommands")] + public bool? IncludeClientCommands { get; set; } - /// Repository owner login (user or organization). - [JsonPropertyName("owner")] - public string Owner { get; set; } = string.Empty; + /// Include enabled user-invocable skills and commands. + [JsonPropertyName("includeSkills")] + public bool? IncludeSkills { get; set; } } -/// Pointer to a GitHub commit. -/// The github_commit variant of . +/// RPC data type for SessionCommandsListRequestWithSession operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubCommit : PushAttachment +internal sealed class SessionCommandsListRequestWithSession { - /// - [JsonIgnore] - public override string Type => "github_commit"; + /// Include runtime built-in commands. + [JsonPropertyName("includeBuiltins")] + public bool? IncludeBuiltins { get; set; } - /// First line of the commit message. - [JsonPropertyName("message")] - public required string Message { get; set; } + /// Include commands registered by protocol clients, including SDK clients and extensions. + [JsonPropertyName("includeClientCommands")] + public bool? IncludeClientCommands { get; set; } - /// Full commit SHA. - [JsonPropertyName("oid")] - public required string Oid { get; set; } + /// Include enabled user-invocable skills and commands. + [JsonPropertyName("includeSkills")] + public bool? IncludeSkills { get; set; } - /// Repository the commit belongs to. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// URL to the commit on GitHub. - [JsonPropertyName("url")] - public required string Url { 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; } -/// Pointer to a GitHub release. -/// The github_release variant of . + +/// Slash-command invocation result containing text output plus Markdown/ANSI rendering flags. +/// The text variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubRelease : PushAttachment +public partial class SlashCommandInvocationResultText : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "github_release"; + public override string Kind => "text"; - /// Human-readable release name. - [JsonPropertyName("name")] - public required string Name { get; set; } + /// Whether text contains Markdown. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("markdown")] + public bool? Markdown { get; set; } - /// Repository the release belongs to. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// Whether ANSI sequences should be preserved. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("preserveAnsi")] + public bool? PreserveAnsi { 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; } - /// Git tag the release is anchored to. - [JsonPropertyName("tagName")] - public required string TagName { 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; } - /// URL to the release on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Text output for the client to render. + [JsonPropertyName("text")] + public required string Text { get; set; } } -/// Pointer to a GitHub Actions job. -/// The github_actions_job variant of . +/// Slash-command invocation result that submits an agent prompt, with display prompt, optional mode, optional user-facing notice, and settings-change flag. +/// The agent-prompt variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubActionsJob : PushAttachment +public partial class SlashCommandInvocationResultAgentPrompt : SlashCommandInvocationResult { /// [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; } + public override string Kind => "agent-prompt"; - /// Job id within the workflow run. - [JsonPropertyName("jobId")] - public required long JobId { get; set; } + /// Prompt text to display to the user. + [JsonPropertyName("displayPrompt")] + public required string DisplayPrompt { get; set; } - /// Display name of the job. - [JsonPropertyName("jobName")] - public required string JobName { get; set; } + /// Optional target session mode for the agent prompt. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("mode")] + public SessionMode? Mode { get; set; } - /// Repository the workflow run belongs to. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// Optional user-facing notice to show before the prompt is submitted. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("notice")] + public string? Notice { get; set; } - /// URL to the job on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Prompt to submit to the agent. + [JsonPropertyName("prompt")] + public required string Prompt { get; set; } - /// Display name of the workflow the job ran in. - [JsonPropertyName("workflowName")] - public required string WorkflowName { 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; } } -/// Pointer to a GitHub repository. -/// The github_repository variant of . +/// Slash-command invocation result indicating completion, with optional message and settings-change flag. +/// The completed variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubRepository : PushAttachment +public partial class SlashCommandInvocationResultCompleted : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "github_repository"; + public override string Kind => "completed"; - /// Short description of the repository. + /// Optional user-facing message describing the completed command. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { get; set; } + [JsonPropertyName("message")] + public string? Message { get; set; } - /// Git ref this attachment is anchored at (branch, tag, or commit). When absent the default branch is implied. + /// Optional target session mode applied without submitting an agent prompt. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("ref")] - public string? Ref { get; set; } - - /// Repository pointer. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + [JsonPropertyName("mode")] + public SessionMode? Mode { get; set; } - /// URL to the repository on GitHub. - [JsonPropertyName("url")] - public required string Url { 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; } } -/// One side of a file diff (head or base). +/// Selectable slash-command subcommand option with name, description, and optional group label. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentGitHubFileDiffSide +public sealed class SlashCommandSelectSubcommandOption { - /// Repository-relative path to the file. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Human-readable description of the subcommand. + [JsonPropertyName("description")] + public string Description { 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; + /// Optional group label for organizing options. + [JsonPropertyName("group")] + public string? Group { get; set; } - /// Repository the file lives in. - [JsonPropertyName("repo")] - public PushGitHubRepoRef Repo { get => field ??= new(); set; } + /// Subcommand name to invoke. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; } -/// Pointer to a single-file diff. At least one of `head` and `base` must be present. -/// The github_file_diff variant of . +/// 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 PushAttachmentGitHubFileDiff : PushAttachment +public partial class SlashCommandInvocationResultSelectSubcommand : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "github_file_diff"; + public override string Kind => "select-subcommand"; - /// File location on the base side of the diff. Absent for additions. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("base")] - public PushAttachmentGitHubFileDiffSide? Base { get; set; } + /// Parent command name that requires subcommand selection. + [JsonPropertyName("command")] + public required string Command { get; set; } - /// File location on the head side of the diff. Absent for deletions. + /// Available subcommand options for the client to present. + [JsonPropertyName("options")] + public required IList Options { get; set; } + + /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("head")] - public PushAttachmentGitHubFileDiffSide? Head { get; set; } + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { 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; } + /// Human-readable title for the selection UI. + [JsonPropertyName("title")] + public required string Title { get; set; } } -/// One side of a tree comparison (head or base). +/// RPC data type for SlashCommandTimelineEntry operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PushAttachmentGitHubTreeComparisonSide +public sealed class SlashCommandTimelineEntry { - /// Repository the revision belongs to. - [JsonPropertyName("repo")] - public PushGitHubRepoRef Repo { get => field ??= new(); set; } + /// What the user must do to recover, when the entry reports a failure the runtime knows an action for. The `text` never names a client affordance, so a client that offers one renders it from this value. + [JsonPropertyName("remediation")] + public RemediationAction? Remediation { get; set; } - /// Git revision (branch, tag, or commit SHA). - [JsonPropertyName("revision")] - public string Revision { get; set; } = string.Empty; + /// Text displayed for the timeline entry. + [JsonPropertyName("text")] + public string Text { get; set; } = string.Empty; + + /// Timeline entry presentation type. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; + + /// Optional URL associated with the timeline entry. + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// Pointer to a comparison between two git revisions. -/// The github_tree_comparison variant of . +/// The add-timeline-entry variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubTreeComparison : PushAttachment +public partial class SlashCommandInvocationResultAddTimelineEntry : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "github_tree_comparison"; + public override string Kind => "add-timeline-entry"; - /// Base side of the comparison. - [JsonPropertyName("base")] - public required PushAttachmentGitHubTreeComparisonSide Base { get; set; } + /// Timeline entry the host should append. + [JsonPropertyName("entry")] + public required SlashCommandTimelineEntry Entry { get; set; } - /// Head side of the comparison. - [JsonPropertyName("head")] - public required PushAttachmentGitHubTreeComparisonSide Head { get; set; } + /// Optional text the host should prefill into the input editor. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("prefillInput")] + public string? PrefillInput { get; set; } - /// URL to the comparison on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } } -/// Generic GitHub URL reference. -/// The github_url variant of . +/// RPC data type for SlashCommandModelPickerDialog operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubUrl : PushAttachment +public sealed class SlashCommandModelPickerDialog { - /// - [JsonIgnore] - public override string Type => "github_url"; + /// Discriminator for a model-picker dialog. + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; - /// URL to the GitHub resource. - [JsonPropertyName("url")] - public required string Url { get; set; } + /// Model that should be enabled before it can be selected. + [JsonPropertyName("modelToEnable")] + public string? ModelToEnable { get; set; } + + /// Settings scope the picker should modify. + [JsonPropertyName("scope")] + public string? Scope { get; set; } + + /// Model-selection target represented by the picker. + [JsonPropertyName("target")] + public string? Target { get; set; } } -/// Pointer to a file in a GitHub repository at a specific ref. -/// The github_file variant of . +/// The show-dialog variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubFile : PushAttachment +public partial class SlashCommandInvocationResultShowDialog : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "github_file"; - - /// Repository-relative path to the file. - [JsonPropertyName("path")] - public required string Path { get; set; } + public override string Kind => "show-dialog"; - /// Git ref the file is read at (branch, tag, or commit SHA). - [JsonPropertyName("ref")] - public required string Ref { get; set; } + /// Dialog the host should display. + [JsonPropertyName("dialog")] + public required SlashCommandModelPickerDialog Dialog { get; set; } - /// Repository the file lives in. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } +} - /// URL to the file on GitHub. - [JsonPropertyName("url")] - public required string Url { get; set; } +/// User-settings snapshot to restore if the host cancels the model switch. +public sealed class SlashCommandInvocationResultSetModelRevertOnCancel +{ } -/// Pointer to a line range inside a file in a GitHub repository. -/// The github_snippet variant of . +/// The set-model variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentGitHubSnippet : PushAttachment +public partial class SlashCommandInvocationResultSetModel : SlashCommandInvocationResult { /// [JsonIgnore] - public override string Type => "github_snippet"; - - /// Line range the snippet covers. - [JsonPropertyName("lineRange")] - public required PushAttachmentFileLineRange LineRange { get; set; } + public override string Kind => "set-model"; - /// Repository-relative path to the file. - [JsonPropertyName("path")] - public required string Path { 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; } - /// Git ref the file is read at (branch, tag, or commit SHA). - [JsonPropertyName("ref")] - public required string Ref { get; set; } + /// Model selected by the command. + [JsonPropertyName("model")] + public required string Model { get; set; } - /// Repository the file lives in. - [JsonPropertyName("repo")] - public required PushGitHubRepoRef Repo { get; set; } + /// Reasoning effort selected for the model. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningEffort")] + public string? ReasoningEffort { get; set; } - /// URL to the snippet on GitHub (with line anchor). - [JsonPropertyName("url")] - public required string Url { get; set; } -} + /// Repository settings scope modified by the command. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("repoScope")] + public string? RepoScope { 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"; + /// User-settings snapshot to restore if the host cancels the model switch. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("revertOnCancel")] + public SlashCommandInvocationResultSetModelRevertOnCancel? RevertOnCancel { get; set; } - /// Base64-encoded content. - [Base64String] - [JsonPropertyName("data")] - public required string Data { get; set; } + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } - /// User-facing display name for the attachment. + /// Settings scope modified by the command. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("displayName")] - public string? DisplayName { get; set; } + [JsonPropertyName("scope")] + public string? Scope { get; set; } - /// MIME type of the inline data. - [JsonPropertyName("mimeType")] - public required string MimeType { get; set; } + /// User-facing warning produced while selecting the model. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("warning")] + public string? Warning { get; set; } } -/// Slim input shape for extension_context attachments; identity fields are runtime-derived. -/// The extension_context variant of . +/// The set-plan-model variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PushAttachmentExtensionContext : PushAttachment +public partial class SlashCommandInvocationResultSetPlanModel : SlashCommandInvocationResult { /// [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; } -} + public override string Kind => "set-plan-model"; -/// 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; } + /// User-facing confirmation message for the plan-model selection. + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Optional canvas instance binding the push for provenance. When supplied, the runtime resolves the canvas, verifies it is owned by the calling extension, and stamps canvasId/instanceId onto each extension_context entry. When omitted, no resolution runs and those fields stay unset on the attachment. - [JsonPropertyName("instanceId")] - public string? InstanceId { get; set; } + /// Dedicated model selected for plan mode. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("planModel")] + public string? PlanModel { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether command execution changed persisted runtime settings. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("runtimeSettingsChanged")] + public bool? RuntimeSettingsChanged { get; set; } } -/// A tool name and arguments to execute through the session's native invocation pipeline. +/// Slash command name and optional raw input string to invoke. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsExecuteRequest +internal sealed class CommandsInvokeRequest { - /// Arguments supplied to the tool. - [JsonPropertyName("arguments")] - public JsonElement Arguments { get; set; } + /// Raw input after the command name. + [JsonPropertyName("input")] + public string? Input { get; set; } - /// Name of the currently offered tool to execute. + /// Command name. Leading slashes are stripped and the name is matched case-insensitively. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; + /// Optional client surface that initiated the invocation. + [JsonPropertyName("origin")] + public CommandsInvocationOrigin? Origin { get; set; } + /// 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. +/// Whether finalizing the invocation effect succeeded, and the failure reason when it did not. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltinToolFormat +internal sealed class CommandsFinalizeInvocationEffectResult { - /// 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; + /// Failure reason when the invocation effect could not be finalized. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Custom input-format discriminator. - [JsonPropertyName("type")] - public BuiltinToolFormatType Type { get; set; } + /// Whether the pending invocation effect was finalized successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// JSON Schema object accepted by a built-in tool. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltinToolInputSchema +/// The slash-command result object that produced the pending effect, echoed back unchanged. +public sealed class CommandsFinalizeInvocationEffectRequestEffect { - /// 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. +/// The pending slash-command invocation effect to finalize, plus whether the host applied or cancelled it. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class BuiltinToolDescriptor +internal sealed class CommandsFinalizeInvocationEffectRequest { - /// 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; } + /// The slash-command result object that produced the pending effect, echoed back unchanged. + [JsonPropertyName("effect")] + public CommandsFinalizeInvocationEffectRequestEffect Effect { get => field ??= new(); set; } - /// Optional human-readable title for the tool. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Whether the host applied or cancelled the pending invocation effect. + [JsonPropertyName("outcome")] + public CommandsInvocationEffectOutcome Outcome { get; set; } - /// Optional tool category discriminator. - [JsonPropertyName("type")] - public string? Type { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Rust-owned built-in tool descriptors for the session. +/// Indicates whether the pending client-handled command was completed successfully. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsGetBuiltinDescriptorsResult +public sealed class CommandsHandlePendingCommandResult { - /// Built-in tool descriptors materialized for the session. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } + /// Whether the command was handled successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Shell-specific names and description lines used to materialize built-in shell tool descriptors. +/// Pending command request ID and an optional error if the client handler failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsShellDescriptorConfig +internal sealed class CommandsHandlePendingCommandRequest { - /// 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; + /// Error message if the command handler failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Stable shell type identifier. - [JsonPropertyName("shellType")] - public string ShellType { get; set; } = string.Empty; + /// Request ID from the command invocation event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Tool name used to stop shell commands. - [JsonPropertyName("stopShellToolName")] - public string StopShellToolName { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Options controlling how Rust-owned built-in tool descriptors are materialized. +/// Error message produced while executing the command, if any. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsGetBuiltinDescriptorsRequest +public sealed class ExecuteCommandResult { - /// Whether background task completion notifications are enabled. - [JsonPropertyName("backgroundTaskNotificationsEnabled")] - public bool? BackgroundTaskNotificationsEnabled { get; set; } + /// Error message produced while executing the command, if any. Omitted when the handler succeeded. + [JsonPropertyName("error")] + public string? Error { get; set; } +} - /// Whether tool descriptors should include authoring metadata. - [JsonPropertyName("includeAuthor")] - public bool? IncludeAuthor { get; set; } +/// Slash command name and argument string to execute synchronously. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ExecuteCommandParams +{ + /// Argument string to pass to the command (empty string if none). + [JsonPropertyName("args")] + public string Args { get; set; } = string.Empty; - /// Whether descriptors should favor fewer user-intervention prompts. - [JsonPropertyName("reduceUserIntervention")] - public bool? ReduceUserIntervention { 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; +} - /// 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; } +/// 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 EnqueueCommandResult +{ + /// The boolean discriminator. + [JsonPropertyName("queued")] + public bool Queued { get; set; } - /// Whether semantic skill lookup is available. - [JsonPropertyName("skillEmbeddingEnabled")] - public bool? SkillEmbeddingEnabled { get; set; } + /// Stable opaque ID of the queued command. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("queueId")] + public string? QueueId { get; set; } } -/// Task completion notification with summary from the agent. +/// Slash-prefixed command string to enqueue for FIFO processing. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskCompleteData +internal sealed class EnqueueCommandParams { - /// 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; } + /// 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; - /// Semantic completion decision. Absent on legacy events and invalid tool calls. - [JsonPropertyName("outcome")] - public TaskCompletionOutcome? Outcome { get; set; } + /// Optional user-facing text for the queue row. The command string is shown when omitted. + [JsonPropertyName("displayText")] + public string? DisplayText { 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Whether the task was accepted as complete. False when validation failed or completion was rejected or blocked by the reviewer. +/// Indicates whether the queued-command response was matched to a pending request. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class CommandsRespondToQueuedCommandResult +{ + /// 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; } - - /// Summary of the completed task, provided by the agent. - [JsonPropertyName("summary")] - public string? Summary { get; set; } + public bool Success { get; set; } } -/// Binary result returned by a tool for the model. +/// Result of the queued command execution. +/// Data type discriminated by handled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExternalToolTextResultForLlmBinaryResultsForLlm +public partial class QueuedCommandResult { - /// Base64-encoded binary data. - [Base64String] - [JsonPropertyName("data")] - public string Data { get; set; } = string.Empty; + /// The boolean discriminator. + [JsonPropertyName("handled")] + public bool Handled { get; set; } - /// Human-readable description of the binary data. - [JsonPropertyName("description")] - public string? Description { 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; } +} - /// Optional metadata from the producing tool. - [JsonPropertyName("metadata")] - public IDictionary? Metadata { get; set; } +/// Queued-command request ID and the result indicating whether the host executed it (and whether to stop processing further queued commands). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class CommandsRespondToQueuedCommandRequest +{ + /// Request ID from the `command.queued` event the host is responding to. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// MIME type of the binary data. - [JsonPropertyName("mimeType")] - public string MimeType { get; set; } = string.Empty; + /// Result of the queued command execution. + [JsonPropertyName("result")] + public QueuedCommandResult Result { get => field ??= new(); set; } - /// Binary result type discriminator. Use "image" for images and "resource" for other binary data. - [JsonPropertyName("type")] - public ExternalToolTextResultForLlmBinaryResultsForLlmType Type { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// A content block within a tool result, which may be text, terminal output, image, audio, or a resource. -/// Polymorphic base type discriminated by type. +/// Telemetry engagement ID for the session, when available. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "type", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentText), "text")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentTerminal), "terminal")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentShellExit), "shell_exit")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentImage), "image")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentAudio), "audio")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentResourceLink), "resource_link")] -[JsonDerivedType(typeof(ExternalToolTextResultForLlmContentResource), "resource")] -public partial class ExternalToolTextResultForLlmContent +public sealed class SessionTelemetryEngagement { - /// The type discriminator. - [JsonPropertyName("type")] - public virtual string Type { get; set; } = string.Empty; + /// Current telemetry engagement ID, when available. + [JsonPropertyName("engagementId")] + public string? EngagementId { get; set; } } - -/// Plain text content block. -/// The text variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentText : ExternalToolTextResultForLlmContent +internal sealed class SessionTelemetryGetEngagementIdRequest { - /// - [JsonIgnore] - public override string Type => "text"; - - /// The text content. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Terminal/shell output content block with optional exit code and working directory. -/// The terminal variant of . +/// Feature override key/value pairs to attach to subsequent telemetry events from this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentTerminal : ExternalToolTextResultForLlmContent +internal sealed class TelemetrySetFeatureOverridesRequest { - /// - [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; } + /// 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; } - /// Terminal/shell output text. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Shell command exit metadata with optional output preview. -/// The shell_exit 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 ExternalToolTextResultForLlmContentShellExit : ExternalToolTextResultForLlmContent +public sealed class UIEphemeralQueryResult { - /// - [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; } + /// Answer returned by the model. + [JsonPropertyName("answer")] + public string Answer { get; set; } = string.Empty; } -/// Image content block with base64-encoded data. -/// The image variant of . +/// Transient question to answer without adding it to conversation history. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentImage : ExternalToolTextResultForLlmContent +internal sealed class UIEphemeralQueryRequest { - /// - [JsonIgnore] - public override string Type => "image"; - - /// Base64-encoded image data. - [Base64String] - [JsonPropertyName("data")] - public required string Data { get; set; } + /// Question to answer from the current conversation context. + [JsonPropertyName("question")] + public string Question { get; set; } = string.Empty; - /// MIME type of the image (e.g., image/png, image/jpeg). - [JsonPropertyName("mimeType")] - public required string MimeType { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Audio content block with base64-encoded data. -/// The audio variant of . +/// The elicitation response (accept with form values, decline, or cancel). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentAudio : ExternalToolTextResultForLlmContent +public sealed class UIElicitationResponse { - /// - [JsonIgnore] - public override string Type => "audio"; + /// MCP response metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } - /// Base64-encoded audio data. - [Base64String] - [JsonPropertyName("data")] - public required string Data { get; set; } + /// The user's response: accept (submitted), decline (rejected), or cancel (dismissed). + [JsonPropertyName("action")] + public UIElicitationResponseAction Action { get; set; } - /// MIME type of the audio (e.g., audio/wav, audio/mpeg). - [JsonPropertyName("mimeType")] - public required string MimeType { get; set; } + /// The form values submitted by the user (present when action is 'accept'). + [JsonPropertyName("content")] + public IDictionary? Content { get; set; } } -/// Icon image for a resource. +/// JSON Schema describing the form fields to present to the user. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExternalToolTextResultForLlmContentResourceLinkIcon +public sealed class UIElicitationSchema { - /// 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; } + /// Form field definitions, keyed by field name. + [JsonPropertyName("properties")] + public IDictionary Properties { get => field ??= new Dictionary(); set; } - /// URL or path to the icon image. - [JsonPropertyName("src")] - public string Src { get; set; } = string.Empty; + /// List of required field names. + [JsonPropertyName("required")] + public IList? Required { get; set; } - /// Theme variant this icon is intended for. - [JsonPropertyName("theme")] - public ExternalToolTextResultForLlmContentResourceLinkIconTheme? Theme { get; set; } + /// Schema type indicator (always 'object'). + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; } -/// Resource link content block referencing an external resource. -/// The resource_link variant of . +/// Metadata controlling an MCP task's lifetime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentResourceLink : ExternalToolTextResultForLlmContent +public sealed class McpTaskMetadata { - /// - [JsonIgnore] - public override string Type => "resource_link"; - - /// Human-readable description of the resource. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("description")] - public string? Description { get; set; } + /// Task time-to-live. + [JsonPropertyName("ttl")] + public long? Ttl { get; set; } +} - /// Icons associated with this resource. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("icons")] - public IList? Icons { get; set; } +/// Prompt message and JSON schema describing the form fields to elicit from the user. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UIElicitationRequest +{ + /// MCP request metadata. + [JsonPropertyName("_meta")] + public IDictionary? Meta { get; set; } - /// MIME type of the resource content. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mimeType")] - public string? MimeType { get; set; } + /// Message describing what information is needed from the user. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; - /// Resource name identifier. - [JsonPropertyName("name")] - public required string Name { get; set; } + /// Elicitation mode. Omitted and form are equivalent for structured elicitation. + [JsonPropertyName("mode")] + public McpElicitationFormMode? Mode { get; set; } - /// Size of the resource in bytes. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("size")] - public long? Size { get; set; } + /// JSON Schema describing the form fields to present to the user. + [JsonPropertyName("requestedSchema")] + public UIElicitationSchema RequestedSchema { get => field ??= new(); set; } - /// Human-readable display title for the resource. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// URI identifying the resource. - [JsonPropertyName("uri")] - public required string Uri { get; set; } + /// MCP task metadata. + [JsonPropertyName("task")] + public McpTaskMetadata? Task { get; set; } } -/// Embedded resource content block with inline text or binary data. -/// The resource variant of . +/// Indicates whether the elicitation response was accepted; false if it was already resolved by another client. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class ExternalToolTextResultForLlmContentResource : ExternalToolTextResultForLlmContent +public sealed class UIElicitationResult { - /// - [JsonIgnore] - public override string Type => "resource"; - - /// The embedded resource contents, either text or base64-encoded binary. - [JsonPropertyName("resource")] - public required JsonElement Resource { get; set; } + /// Whether the response was accepted. False if the request was already resolved by another client. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// A message injected by a tool result. +/// Pending elicitation request ID and the user's response (accept/decline/cancel + form values). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolResultNewMessage +internal sealed class UIHandlePendingElicitationRequest { - /// Message content to inject after the tool result. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// The unique request ID from the elicitation.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Source attributed to the injected message. - [JsonPropertyName("source")] - public string Source { get; set; } = string.Empty; + /// The elicitation response (accept with form values, decline, or cancel). + [JsonPropertyName("result")] + public UIElicitationResponse Result { get => field ??= new(); set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// RPC data type for TaskCompletionDecision operations. +/// Indicates whether the pending UI request was resolved by this call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class TaskCompletionDecision +public sealed class UIHandlePendingResult { - /// 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; } + /// 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; } } -/// Expanded canonical result returned by a session tool. +/// 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 ToolResultExpanded +public sealed class UIUserInputResponse { - /// 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; } + /// The user's answer text. + [JsonPropertyName("answer")] + public string Answer { get; set; } = string.Empty; - /// Error message for an unsuccessful execution. - [JsonPropertyName("error")] - public string? Error { 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; } +} - /// Metadata propagated with the tool result, including information-flow labels. - [JsonPropertyName("mcpMeta")] - public IDictionary? McpMeta { get; set; } +/// Request ID of a pending `user_input.requested` event and the user's response. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class UIHandlePendingUserInputRequest +{ + /// The unique request ID from the user_input.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Messages to inject after the tool result. - [JsonPropertyName("newMessages")] - public IList? NewMessages { 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; } - /// Whether post-tool-use failure hooks have already processed this result. - [JsonPropertyName("postToolUseFailureHooksProcessed")] - public bool? PostToolUseFailureHooksProcessed { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Execution outcome classification. - [JsonPropertyName("resultType")] - public ToolResultType ResultType { get; set; } +/// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class UIHandlePendingSamplingResponse +{ +} - /// Detailed log content available for session display. - [JsonPropertyName("sessionLog")] - public string? SessionLog { 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; - /// Skill invocation metadata produced by the tool. - [JsonPropertyName("skillInvocation")] - public JsonElement? SkillInvocation { get; set; } + /// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. + [JsonPropertyName("response")] + public UIHandlePendingSamplingResponse? Response { get; set; } - /// Whether large-output post-processing should be skipped. - [JsonPropertyName("skipLargeOutputProcessing")] - public bool? SkipLargeOutputProcessing { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Structured result content in addition to the model-facing text. - [JsonPropertyName("structuredContent")] - public JsonElement? StructuredContent { 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 +{ + /// The unique request ID from the auto_mode_switch.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; - /// Completion-review decision produced by the task-completion tool. - [JsonPropertyName("taskCompletionDecision")] - public TaskCompletionDecision? TaskCompletionDecision { 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; } - /// Text result returned to the model. - [JsonPropertyName("textResultForLlm")] - public string TextResultForLlm { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Deferred tool names made available by this result. - [JsonPropertyName("toolReferences")] - public IList? ToolReferences { 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; } - /// Tool-specific telemetry payload. - [JsonPropertyName("toolTelemetry")] - public JsonElement? ToolTelemetry { get; set; } + /// AI Credits to add to the current max when action is 'add'. + [JsonPropertyName("additionalAiCredits")] + public double? AdditionalAiCredits { get; set; } - /// Optional UI resource produced by the tool. - [JsonPropertyName("uiResource")] - public JsonElement? UiResource { get; set; } + /// New absolute max AI Credits when action is 'set'. + [JsonPropertyName("maxAiCredits")] + public double? MaxAiCredits { get; set; } } -/// Task-completion tool arguments and final result used to build a label-safe session event payload. +/// 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 ToolsTaskCompleteEventDataRequest +internal sealed class UIHandlePendingSessionLimitsExhaustedRequest { - /// Final expanded result returned by the task_complete tool. - [JsonPropertyName("finalResult")] - public ToolResultExpanded FinalResult { get => field ??= new(); set; } + /// The unique request ID from the session_limits_exhausted.requested event. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; + + /// The selected session-limit action. + [JsonPropertyName("response")] + public UISessionLimitsExhaustedResponse Response { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// 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. +/// 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 HandlePendingToolCallResult +public sealed class UIExitPlanModeResponse { - /// Whether the tool call result was handled successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether the plan was approved. + [JsonPropertyName("approved")] + public bool Approved { get; set; } + + /// Whether subsequent edits should be auto-approved without confirmation. + [JsonPropertyName("autoApproveEdits")] + public bool? AutoApproveEdits { get; set; } + + /// When true, the agent is instructed to end its turn without starting implementation so the client can restore the session model and auto-submit a fresh implementation turn on it. Set only when a distinct plan configuration (a different model, reasoning effort, or context tier) actually ran the planning turn. + [JsonPropertyName("deferImplementation")] + public bool? DeferImplementation { get; set; } + + /// Feedback from the user when they declined the plan or requested changes. + [JsonPropertyName("feedback")] + public string? Feedback { get; set; } + + /// The action the user selected. Defaults to 'autopilot' when autoApproveEdits is true, otherwise 'interactive'. + [JsonPropertyName("selectedAction")] + public UIExitPlanModeAction? SelectedAction { get; set; } } -/// Pending external tool call request ID, with the tool result or an error describing why it failed. +/// Request ID of a pending `exit_plan_mode.requested` event and the user's response. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class HandlePendingToolCallRequest +internal sealed class UIHandlePendingExitPlanModeRequest { - /// Error message if the tool call failed. - [JsonPropertyName("error")] - public string? Error { get; set; } - - /// Request ID of the pending tool call. + /// The unique request ID from the exit_plan_mode.requested event. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; - /// Tool call result (string or expanded result object). - [JsonPropertyName("result")] - public JsonElement? Result { 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; } -/// 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. +/// 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 ToolsInitializeAndValidateResult +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 SessionToolsInitializeAndValidateRequest +internal sealed class SessionUiRegisterDirectAutoModeSwitchHandlerRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Lightweight metadata for a currently initialized session tool. +/// Indicates whether the handle was active and the registration count was decremented. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CurrentToolMetadata +public sealed class UIUnregisterDirectAutoModeSwitchHandlerResult { - /// 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; + /// True if the handle was active and decremented the counter; false if the handle was unknown. + [JsonPropertyName("unregistered")] + public bool Unregistered { get; set; } +} - /// JSON Schema for tool input. - [JsonPropertyName("input_schema")] - public IDictionary? InputSchema { 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; - /// MCP server name for MCP-backed tools. - [JsonPropertyName("mcpServerName")] - public string? McpServerName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Raw MCP tool name for MCP-backed tools. - [JsonPropertyName("mcpToolName")] - public string? McpToolName { get; set; } +/// Indicates whether the operation succeeded. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsConfigureResult +{ + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} - /// Model-facing tool name. +/// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRuleSource +{ + /// Name of the policy source. [JsonPropertyName("name")] public string Name { get; set; } = string.Empty; - /// Optional MCP/config namespaced tool name. - [JsonPropertyName("namespacedName")] - public string? NamespacedName { get; set; } + /// Type of the policy source. + [JsonPropertyName("type")] + public string Type { get; set; } = string.Empty; } -/// Current lightweight tool metadata snapshot for the session. +/// Single content-exclusion rule supplied to `session.permissions.configure`, with paths, match conditions, and source. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsGetCurrentMetadataResult +public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRule { - /// Current tool metadata, or null when tools have not been initialized yet. - [JsonPropertyName("tools")] - public IList? Tools { get; set; } + /// 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; } } -/// Identifies the target session. +/// Content-exclusion policy supplied to `session.permissions.configure`, with rules, last-updated data, and scope. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionToolsGetCurrentMetadataRequest +public sealed class PermissionsConfigureAdditionalContentExclusionPolicy +{ + /// 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 `PermissionsConfigureAdditionalContentExclusionPolicyScope` enumeration. + [JsonPropertyName("scope")] + public PermissionsConfigureAdditionalContentExclusionPolicyScope Scope { 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionPathsConfig +{ + /// 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; } +} + +/// 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; } + + /// Rules that auto-deny matching requests. + [JsonPropertyName("denied")] + public IList Denied { get => field ??= []; 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; } + + /// 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; } +} + +/// 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; } + + /// If specified, sets whether path/URL read permission requests are auto-approved. Omit to leave the current value unchanged. + [JsonPropertyName("approveAllReadPermissionRequests")] + public bool? ApproveAllReadPermissionRequests { get; set; } + + /// If specified, sets whether tool permission requests are auto-approved without prompting. Omit to leave the current value unchanged. + [JsonPropertyName("approveAllToolPermissionRequests")] + public bool? ApproveAllToolPermissionRequests { get; set; } + + /// If specified, replaces the session's path-permission policy. The runtime constructs the appropriate PathManager based on these inputs (rooted at the session's working directory). Omit to leave the current path policy unchanged. + [JsonPropertyName("paths")] + public PermissionPathsConfig? Paths { get; set; } + + /// If specified, replaces the session's approved/denied permission rules. Omit to leave the current rules unchanged. + [JsonPropertyName("rules")] + public PermissionRulesSet? Rules { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// If specified, replaces the session's URL-permission policy. The runtime constructs a fresh DefaultUrlManager based on these inputs. Omit to leave the current URL policy unchanged. + [JsonPropertyName("urls")] + public PermissionUrlsConfig? Urls { get; set; } } -/// Empty result after replacing the calling connection's externally implemented tools. +/// Indicates whether the permission decision was applied; false when the request was already resolved. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsSetResult +public sealed class PermissionRequestResult { + /// Whether the permission request was handled successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Serializable definition of a caller-implemented tool whose execution is handled over the SDK connection. +/// The client's response to the pending permission prompt. +/// Polymorphic base type discriminated by kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ProtocolExternalToolDefinition +[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 { - /// 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; + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { 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; } +/// Permission-decision request variant to approve only the current permission request. +/// The approve-once variant of . +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public partial class PermissionDecisionApproveOnce : PermissionDecision +{ + /// + [JsonIgnore] + public override string Kind => "approve-once"; - /// Unique model-visible tool name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// 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; } +} - /// Whether this definition replaces a built-in tool with the same name. - [JsonPropertyName("overridesBuiltInTool")] - public bool? OverridesBuiltInTool { get; set; } +/// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). +/// Polymorphic base type discriminated by kind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "kind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalCommands), "commands")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalRead), "read")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalWrite), "write")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMcp), "mcp")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMcpSampling), "mcp-sampling")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalMemory), "memory")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalCustomTool), "custom-tool")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionManagement), "extension-management")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalWorkflow), "workflow")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess), "extension-permission-access")] +[JsonDerivedType(typeof(PermissionDecisionApproveForSessionApprovalExtensionEnvAccess), "extension-env-access")] +public partial class PermissionDecisionApproveForSessionApproval +{ + /// The type discriminator. + [JsonPropertyName("kind")] + public virtual string Kind { get; set; } = string.Empty; +} - /// 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; } +/// 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"; - /// Optional human-readable display title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// Command identifiers covered by this approval. + [JsonPropertyName("commandIdentifiers")] + public required IList CommandIdentifiers { get; set; } } -/// Complete externally implemented tool list for the calling connection. An empty list removes every tool previously supplied by that connection. +/// Session-scoped approval details for read-only filesystem operations. +/// The read variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ToolsSetRequest +public partial class PermissionDecisionApproveForSessionApprovalRead : PermissionDecisionApproveForSessionApproval { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "read"; +} - /// Complete replacement list for the calling connection. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; 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"; } -/// Empty result after applying subagent settings. +/// Session-scoped approval details for an MCP server tool, or all tools on the server when `toolName` is null. +/// The mcp variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ToolsUpdateSubagentSettingsResult +public partial class PermissionDecisionApproveForSessionApprovalMcp : PermissionDecisionApproveForSessionApproval { + /// + [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; } } -/// Subagent model, reasoning effort, context tier, and auto-invocation settings. +/// Session-scoped approval details for MCP sampling requests from a server. +/// The mcp-sampling variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SubagentSettingsEntry +public partial class PermissionDecisionApproveForSessionApprovalMcpSampling : PermissionDecisionApproveForSessionApproval { - /// 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; } + /// + [JsonIgnore] + public override string Kind => "mcp-sampling"; - /// Context tier override for matching subagents. - [JsonPropertyName("contextTier")] - public SubagentSettingsEntryContextTier? ContextTier { get; set; } + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } +} - /// Reasoning effort override for matching subagents. - [JsonPropertyName("effortLevel")] - public string? EffortLevel { 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"; +} - /// Model override for matching subagents. - [JsonPropertyName("model")] - public string? Model { 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"; - /// Whether the configured model strategy is preferred or required. - [JsonPropertyName("modelPolicy")] - public AgentModelPolicy? ModelPolicy { get; set; } + /// Custom tool name. + [JsonPropertyName("toolName")] + public required string ToolName { get; set; } } -/// Configured per-agent subagent overrides. -public sealed class UpdateSubagentSettingsRequestSubagents +/// 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 { - /// Per-agent settings keyed by subagent agent_type. - [JsonPropertyName("agents")] - public IDictionary? Agents { get; set; } + /// + [JsonIgnore] + public override string Kind => "extension-management"; - /// Names of subagents the user has turned off; they cannot be dispatched. - [JsonPropertyName("disabledSubagents")] - public IList? DisabledSubagents { 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; } +} - /// Maximum number of subagents that can run concurrently; applies to usage-based billing users only. - [JsonPropertyName("maxConcurrency")] - public int? MaxConcurrency { 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 +{ + /// + [JsonIgnore] + public override string Kind => "workflow"; - /// Maximum subagent nesting depth; applies to usage-based billing users only. - [JsonPropertyName("maxDepth")] - public int? MaxDepth { 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; } } -/// Subagent settings to apply to the current session. +/// 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)] -internal sealed class UpdateSubagentSettingsRequest +public partial class PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess : PermissionDecisionApproveForSessionApproval { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "extension-permission-access"; - /// Subagent settings to apply, or null to clear the live session override. - [JsonPropertyName("subagents")] - public UpdateSubagentSettingsRequestSubagents? Subagents { get; set; } + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// RPC data type for SessionCommandsList operations. +/// 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 sealed class SessionCommandsListRequest +public partial class PermissionDecisionApproveForSessionApprovalExtensionEnvAccess : PermissionDecisionApproveForSessionApproval { - /// Include runtime built-in commands. - [JsonPropertyName("includeBuiltins")] - public bool? IncludeBuiltins { get; set; } + /// + [JsonIgnore] + public override string Kind => "extension-env-access"; - /// Include commands registered by protocol clients, including SDK clients and extensions. - [JsonPropertyName("includeClientCommands")] - public bool? IncludeClientCommands { get; set; } + /// Names of the sensitive environment variables this approval covers. Values are never persisted. + [JsonPropertyName("environmentVariables")] + public required IList EnvironmentVariables { get; set; } - /// Include enabled user-invocable skills and commands. - [JsonPropertyName("includeSkills")] - public bool? IncludeSkills { get; set; } + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// RPC data type for SessionCommandsListRequestWithSession operations. +/// 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)] -internal sealed class SessionCommandsListRequestWithSession +public partial class PermissionDecisionApproveForSession : PermissionDecision { - /// Include runtime built-in commands. - [JsonPropertyName("includeBuiltins")] - public bool? IncludeBuiltins { get; set; } + /// + [JsonIgnore] + public override string Kind => "approve-for-session"; - /// Include commands registered by protocol clients, including SDK clients and extensions. - [JsonPropertyName("includeClientCommands")] - public bool? IncludeClientCommands { 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; } - /// Include enabled user-invocable skills and commands. - [JsonPropertyName("includeSkills")] - public bool? IncludeSkills { get; 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; } +} - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { 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"; + + /// Canonical directories covered by the read-only grant. + [JsonPropertyName("directories")] + public required IList Directories { get; set; } } -/// Result of invoking the slash command (text output, prompt to send to the agent, completion, or subcommand selection). +/// 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(SlashCommandInvocationResultText), "text")] -[JsonDerivedType(typeof(SlashCommandInvocationResultAgentPrompt), "agent-prompt")] -[JsonDerivedType(typeof(SlashCommandInvocationResultCompleted), "completed")] -[JsonDerivedType(typeof(SlashCommandInvocationResultSelectSubcommand), "select-subcommand")] -[JsonDerivedType(typeof(SlashCommandInvocationResultAddTimelineEntry), "add-timeline-entry")] -[JsonDerivedType(typeof(SlashCommandInvocationResultShowDialog), "show-dialog")] -[JsonDerivedType(typeof(SlashCommandInvocationResultSetModel), "set-model")] -[JsonDerivedType(typeof(SlashCommandInvocationResultSetPlanModel), "set-plan-model")] -public partial class SlashCommandInvocationResult +[JsonDerivedType(typeof(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")] @@ -19759,822 +22949,630 @@ public partial class SlashCommandInvocationResult } -/// Slash-command invocation result containing text output plus Markdown/ANSI rendering flags. -/// The text variant of . +/// Location-scoped approval details for specific command identifiers. +/// The commands variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultText : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForLocationApprovalCommands : PermissionDecisionApproveForLocationApproval { /// [JsonIgnore] - public override string Kind => "text"; - - /// Whether text contains Markdown. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("markdown")] - public bool? Markdown { get; set; } - - /// Whether ANSI sequences should be preserved. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("preserveAnsi")] - public bool? PreserveAnsi { 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; } - - /// 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; } + public override string Kind => "commands"; - /// Text output for the client to render. - [JsonPropertyName("text")] - public required string Text { get; set; } + /// Command identifiers covered by this approval. + [JsonPropertyName("commandIdentifiers")] + public required IList CommandIdentifiers { get; set; } } -/// Slash-command invocation result that submits an agent prompt, with display prompt, optional mode, optional user-facing notice, and settings-change flag. -/// The agent-prompt variant of . +/// Location-scoped approval details for read-only filesystem operations. +/// The read variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultAgentPrompt : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForLocationApprovalRead : PermissionDecisionApproveForLocationApproval { /// [JsonIgnore] - public override string Kind => "agent-prompt"; - - /// Prompt text to display to the user. - [JsonPropertyName("displayPrompt")] - public required string DisplayPrompt { get; set; } - - /// Optional target session mode for the agent prompt. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mode")] - public SessionMode? Mode { get; set; } - - /// Optional user-facing notice to show before the prompt is submitted. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("notice")] - public string? Notice { get; set; } - - /// Prompt to submit to the agent. - [JsonPropertyName("prompt")] - public required string Prompt { get; set; } - - /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + public override string Kind => "read"; } -/// Slash-command invocation result indicating completion, with optional message and settings-change flag. -/// The completed variant of . +/// Location-scoped approval details for filesystem write operations. +/// The write variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultCompleted : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForLocationApprovalWrite : PermissionDecisionApproveForLocationApproval { /// [JsonIgnore] - public override string Kind => "completed"; - - /// Optional user-facing message describing the completed command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } - - /// Optional target session mode applied without submitting an agent prompt. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("mode")] - public SessionMode? Mode { get; set; } - - /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + public override string Kind => "write"; } -/// Selectable slash-command subcommand option with name, description, and optional group label. +/// 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 SlashCommandSelectSubcommandOption +public partial class PermissionDecisionApproveForLocationApprovalMcp : PermissionDecisionApproveForLocationApproval { - /// Human-readable description of the subcommand. - [JsonPropertyName("description")] - public string Description { get; set; } = string.Empty; + /// + [JsonIgnore] + public override string Kind => "mcp"; - /// Optional group label for organizing options. - [JsonPropertyName("group")] - public string? Group { get; set; } + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } - /// Subcommand name to invoke. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// MCP tool name, or null to cover every tool on the server. + [JsonPropertyName("toolName")] + public string? ToolName { get; set; } } -/// Slash-command invocation result asking the client to present subcommand options for a parent command. -/// The select-subcommand variant of . +/// Location-scoped approval details for MCP sampling requests from a server. +/// The mcp-sampling variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultSelectSubcommand : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForLocationApprovalMcpSampling : PermissionDecisionApproveForLocationApproval { /// [JsonIgnore] - public override string Kind => "select-subcommand"; - - /// Parent command name that requires subcommand selection. - [JsonPropertyName("command")] - public required string Command { get; set; } - - /// Available subcommand options for the client to present. - [JsonPropertyName("options")] - public required IList Options { get; set; } - - /// True when the invocation mutated user runtime settings; consumers caching settings should refresh. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + public override string Kind => "mcp-sampling"; - /// Human-readable title for the selection UI. - [JsonPropertyName("title")] - public required string Title { get; set; } + /// MCP server name. + [JsonPropertyName("serverName")] + public required string ServerName { get; set; } } -/// RPC data type for SlashCommandTimelineEntry operations. +/// Location-scoped approval details for writes to long-term memory. +/// The memory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SlashCommandTimelineEntry +public partial class PermissionDecisionApproveForLocationApprovalMemory : PermissionDecisionApproveForLocationApproval { - /// What the user must do to recover, when the entry reports a failure the runtime knows an action for. The `text` never names a client affordance, so a client that offers one renders it from this value. - [JsonPropertyName("remediation")] - public RemediationAction? Remediation { get; set; } - - /// Text displayed for the timeline entry. - [JsonPropertyName("text")] - public string Text { get; set; } = string.Empty; - - /// Timeline entry presentation type. - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; - - /// Optional URL associated with the timeline entry. - [JsonPropertyName("url")] - public string? Url { get; set; } + /// + [JsonIgnore] + public override string Kind => "memory"; } -/// The add-timeline-entry variant of . +/// Location-scoped approval details for a custom tool, keyed by tool name. +/// The custom-tool variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultAddTimelineEntry : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForLocationApprovalCustomTool : PermissionDecisionApproveForLocationApproval { /// [JsonIgnore] - public override string Kind => "add-timeline-entry"; - - /// Timeline entry the host should append. - [JsonPropertyName("entry")] - public required SlashCommandTimelineEntry Entry { get; set; } - - /// Optional text the host should prefill into the input editor. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("prefillInput")] - public string? PrefillInput { get; set; } + public override string Kind => "custom-tool"; - /// Whether command execution changed persisted runtime settings. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + /// Custom tool name. + [JsonPropertyName("toolName")] + public required string ToolName { get; set; } } -/// RPC data type for SlashCommandModelPickerDialog operations. +/// 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 SlashCommandModelPickerDialog +public partial class PermissionDecisionApproveForLocationApprovalExtensionManagement : PermissionDecisionApproveForLocationApproval { - /// Discriminator for a model-picker dialog. - [JsonPropertyName("kind")] - public string Kind { get; set; } = string.Empty; - - /// Model that should be enabled before it can be selected. - [JsonPropertyName("modelToEnable")] - public string? ModelToEnable { get; set; } - - /// Settings scope the picker should modify. - [JsonPropertyName("scope")] - public string? Scope { get; set; } + /// + [JsonIgnore] + public override string Kind => "extension-management"; - /// Model-selection target represented by the picker. - [JsonPropertyName("target")] - public string? Target { get; set; } + /// Optional operation identifier; when omitted, the approval covers all extension management operations. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("operation")] + public string? Operation { get; set; } } -/// The show-dialog variant of . +/// Location-scoped workflow approval, optionally narrowed by approval key. +/// The workflow variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SlashCommandInvocationResultShowDialog : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForLocationApprovalWorkflow : PermissionDecisionApproveForLocationApproval { /// [JsonIgnore] - public override string Kind => "show-dialog"; - - /// Dialog the host should display. - [JsonPropertyName("dialog")] - public required SlashCommandModelPickerDialog Dialog { get; set; } + public override string Kind => "workflow"; - /// Whether command execution changed persisted runtime settings. + /// Optional workflow operation name or canonical approval key; when omitted, the approval covers all workflow operations. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + [JsonPropertyName("approvalKey")] + public string? ApprovalKey { get; set; } } -/// User-settings snapshot to restore if the host cancels the model switch. -public sealed class SlashCommandInvocationResultSetModelRevertOnCancel +/// 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 partial class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess : PermissionDecisionApproveForLocationApproval { + /// + [JsonIgnore] + public override string Kind => "extension-permission-access"; + + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// The set-model variant of . +/// 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 partial class SlashCommandInvocationResultSetModel : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForLocationApprovalExtensionEnvAccess : PermissionDecisionApproveForLocationApproval { /// [JsonIgnore] - public override string Kind => "set-model"; - - /// Auto routing profile selected by the command, when the model is Auto. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("autoTier")] - public AutoTier? AutoTier { get; set; } - - /// Model selected by the command. - [JsonPropertyName("model")] - public required string Model { get; set; } - - /// Reasoning effort selected for the model. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reasoningEffort")] - public string? ReasoningEffort { get; set; } - - /// Repository settings scope modified by the command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("repoScope")] - public string? RepoScope { get; set; } - - /// User-settings snapshot to restore if the host cancels the model switch. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("revertOnCancel")] - public SlashCommandInvocationResultSetModelRevertOnCancel? RevertOnCancel { get; set; } - - /// Whether command execution changed persisted runtime settings. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + public override string Kind => "extension-env-access"; - /// Settings scope modified by the command. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("scope")] - public string? Scope { get; set; } + /// Names of the sensitive environment variables this approval covers. Values are never persisted. + [JsonPropertyName("environmentVariables")] + public required IList EnvironmentVariables { get; set; } - /// User-facing warning produced while selecting the model. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("warning")] - public string? Warning { get; set; } + /// Extension name. + [JsonPropertyName("extensionName")] + public required string ExtensionName { get; set; } } -/// The set-plan-model variant of . +/// Permission-decision request variant to approve 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 SlashCommandInvocationResultSetPlanModel : SlashCommandInvocationResult +public partial class PermissionDecisionApproveForLocation : PermissionDecision { /// [JsonIgnore] - public override string Kind => "set-plan-model"; - - /// User-facing confirmation message for the plan-model selection. - [JsonPropertyName("message")] - public required string Message { get; set; } + public override string Kind => "approve-for-location"; - /// Dedicated model selected for plan mode. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("planModel")] - public string? PlanModel { get; set; } + /// Approval to persist for this location. + [JsonPropertyName("approval")] + public required PermissionDecisionApproveForLocationApproval Approval { get; set; } - /// Whether command execution changed persisted runtime settings. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("runtimeSettingsChanged")] - public bool? RuntimeSettingsChanged { get; set; } + /// Location key (git root or cwd) to persist the approval to. + [JsonPropertyName("locationKey")] + public required string LocationKey { get; set; } } -/// Slash command name and optional raw input string to invoke. +/// 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 CommandsInvokeRequest +public partial class PermissionDecisionApprovePermanently : PermissionDecision { - /// Raw input after the command name. - [JsonPropertyName("input")] - public string? Input { get; set; } - - /// Command name. Leading slashes are stripped and the name is matched case-insensitively. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; - - /// Optional client surface that initiated the invocation. - [JsonPropertyName("origin")] - public CommandsInvocationOrigin? Origin { get; set; } + /// + [JsonIgnore] + public override string Kind => "approve-permanently"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// URL domain to approve permanently. + [JsonPropertyName("domain")] + public required string Domain { get; set; } } -/// Whether finalizing the invocation effect succeeded, and the failure reason when it did not. +/// Permission-decision request variant to reject a pending permission request, with optional feedback. +/// The reject variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsFinalizeInvocationEffectResult +public partial class PermissionDecisionReject : PermissionDecision { - /// Failure reason when the invocation effect could not be finalized. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// + [JsonIgnore] + public override string Kind => "reject"; - /// Whether the pending invocation effect was finalized successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Optional feedback explaining the rejection. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("feedback")] + public string? Feedback { get; set; } } -/// The slash-command result object that produced the pending effect, echoed back unchanged. -public sealed class CommandsFinalizeInvocationEffectRequestEffect +/// 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"; } -/// The pending slash-command invocation effect to finalize, plus whether the host applied or cancelled it. +/// Permission-decision variant indicating the request was approved. +/// The approved variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsFinalizeInvocationEffectRequest +public partial class PermissionDecisionApproved : PermissionDecision { - /// The slash-command result object that produced the pending effect, echoed back unchanged. - [JsonPropertyName("effect")] - public CommandsFinalizeInvocationEffectRequestEffect Effect { get => field ??= new(); set; } + /// + [JsonIgnore] + public override string Kind => "approved"; +} - /// Whether the host applied or cancelled the pending invocation effect. - [JsonPropertyName("outcome")] - public CommandsInvocationEffectOutcome Outcome { 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"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The approval to add as a session-scoped rule. + [JsonPropertyName("approval")] + public required UserToolSessionApproval Approval { get; set; } } -/// Indicates whether the pending client-handled command was completed successfully. +/// 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 sealed class CommandsHandlePendingCommandResult +public partial class PermissionDecisionApprovedForLocation : PermissionDecision { - /// Whether the command was handled successfully. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// + [JsonIgnore] + public override string Kind => "approved-for-location"; + + /// The approval to persist for this location. + [JsonPropertyName("approval")] + public required UserToolSessionApproval Approval { get; set; } + + /// The location key (git root or cwd) to persist the approval to. + [JsonPropertyName("locationKey")] + public required string LocationKey { get; set; } } -/// Pending command request ID and an optional error if the client handler failed. +/// Permission-decision variant indicating the request was cancelled before use, with an optional reason. +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsHandlePendingCommandRequest +public partial class PermissionDecisionCancelled : PermissionDecision { - /// 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; + /// + [JsonIgnore] + public override string Kind => "cancelled"; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Optional explanation of why the request was cancelled. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reason")] + public string? Reason { get; set; } } -/// Error message produced while executing the command, if any. +/// 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 sealed class ExecuteCommandResult +public partial class PermissionDecisionDeniedByRules : PermissionDecision { - /// Error message produced while executing the command, if any. Omitted when the handler succeeded. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// + [JsonIgnore] + public override string Kind => "denied-by-rules"; + + /// Rules that denied the request. + [JsonPropertyName("rules")] + public required IList Rules { get; set; } } -/// Slash command name and argument string to execute synchronously. +/// 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)] -internal sealed class ExecuteCommandParams +public partial class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser : PermissionDecision { - /// Argument string to pass to the command (empty string if none). - [JsonPropertyName("args")] - public string Args { get; set; } = string.Empty; - - /// 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; + /// + [JsonIgnore] + public override string Kind => "denied-no-approval-rule-and-could-not-request-from-user"; } -/// Indicates whether the command was accepted into the local execution queue. -/// Data type discriminated by queued. +/// 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 EnqueueCommandResult +public partial class PermissionDecisionDeniedInteractivelyByUser : PermissionDecision { - /// The boolean discriminator. - [JsonPropertyName("queued")] - public bool Queued { get; set; } + /// + [JsonIgnore] + public override string Kind => "denied-interactively-by-user"; - /// Stable opaque ID of the queued command. + /// Optional feedback from the user explaining the denial. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("queueId")] - public string? QueueId { get; set; } + [JsonPropertyName("feedback")] + public string? Feedback { get; set; } + + /// Whether to force-reject the current agent turn. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("forceReject")] + public bool? ForceReject { get; set; } } -/// Slash-prefixed command string to enqueue for FIFO processing. +/// Permission-decision variant indicating denial by content-exclusion policy, with path and message. +/// The denied-by-content-exclusion-policy variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EnqueueCommandParams +public partial class PermissionDecisionDeniedByContentExclusionPolicy : PermissionDecision { - /// 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; + /// + [JsonIgnore] + public override string Kind => "denied-by-content-exclusion-policy"; - /// Optional user-facing text for the queue row. The command string is shown when omitted. - [JsonPropertyName("displayText")] - public string? DisplayText { get; set; } + /// Human-readable explanation of why the path was excluded. + [JsonPropertyName("message")] + public required string Message { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// File path that triggered the exclusion. + [JsonPropertyName("path")] + public required string Path { get; set; } } -/// Indicates whether the queued-command response was matched to a pending request. +/// Permission-decision variant indicating denial by a permission request hook, with optional message and interrupt flag. +/// The denied-by-permission-request-hook variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CommandsRespondToQueuedCommandResult +public partial class PermissionDecisionDeniedByPermissionRequestHook : PermissionDecision { - /// Whether a pending queued command with the given request ID was found and resolved. False when the request was already resolved, cancelled, or unknown. - [JsonPropertyName("success")] - public bool Success { get; set; } -} + /// + [JsonIgnore] + public override string Kind => "denied-by-permission-request-hook"; -/// Result of the queued command execution. -/// Data type discriminated by handled. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class QueuedCommandResult -{ - /// The boolean discriminator. - [JsonPropertyName("handled")] - public bool Handled { get; set; } + /// Whether to interrupt the current agent turn. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("interrupt")] + public bool? Interrupt { get; set; } - /// When true, the runtime will not process subsequent queued commands until a new request comes in. + /// Optional message from the hook explaining the denial. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("stopProcessingQueue")] - public bool? StopProcessingQueue { get; set; } + [JsonPropertyName("message")] + public string? Message { get; set; } } -/// Queued-command request ID and the result indicating whether the host executed it (and whether to stop processing further queued commands). +/// Pending permission request ID and the decision to apply (approve/reject and scope). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class CommandsRespondToQueuedCommandRequest +internal sealed class PermissionDecisionRequest { - /// Request ID from the `command.queued` event the host is responding to. + /// Optional informational context describing how and where this response was made. Omit it to preserve legacy behavior without attributing an origin. + [JsonPropertyName("decisionContext")] + public PermissionDecisionContext? DecisionContext { get; set; } + + /// Request ID of the pending permission request. [JsonPropertyName("requestId")] public string RequestId { get; set; } = string.Empty; - /// Result of the queued command execution. + /// The client's response to the pending permission prompt. [JsonPropertyName("result")] - public QueuedCommandResult Result { get => field ??= new(); set; } + public PermissionDecision Result { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Telemetry engagement ID for the session, when available. +/// Pending permission prompt reconstructed from event history, with request ID and user-facing prompt details. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionTelemetryEngagement +public sealed class PendingPermissionRequest { - /// Current telemetry engagement ID, when available. - [JsonPropertyName("engagementId")] - public string? EngagementId { get; set; } + /// Permission-recovery episode that authorized this request to surface for interactive attention. + [JsonPropertyName("recoveryEpisodeId")] + public string? RecoveryEpisodeId { get; set; } + + /// The user-facing permission prompt details (commands, write, read, mcp, url, memory, custom-tool, path, hook). + [JsonPropertyName("request")] + public PermissionPromptRequest Request { get; set; } = null!; + + /// Unique identifier for the pending permission request. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; } -/// Identifies the target session. +/// List of pending permission requests reconstructed from event history. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionTelemetryGetEngagementIdRequest +public sealed class PendingPermissionRequestList { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } } -/// Feature override key/value pairs to attach to subsequent telemetry events from this session. +/// No parameters; returns currently-pending permission requests for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class TelemetrySetFeatureOverridesRequest +internal sealed class PermissionsPendingRequestsRequest { - /// 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; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Completed transient query. Ordered chunks and the terminal outcome are also delivered through `ui.ephemeral_query` session events while it runs. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIEphemeralQueryResult +public sealed class PermissionsSetApproveAllResult { - /// Answer returned by the model. - [JsonPropertyName("answer")] - public string Answer { get; set; } = string.Empty; + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Transient question to answer without adding it to conversation history. +/// Allow-all toggle for tool permission requests, with an optional telemetry source. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIEphemeralQueryRequest +internal sealed class PermissionsSetApproveAllRequest { - /// Question to answer from the current conversation context. - [JsonPropertyName("question")] - public string Question { get; set; } = string.Empty; + /// Whether to auto-approve all tool permission requests. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; -} - -/// The elicitation response (accept with form values, decline, or cancel). -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIElicitationResponse -{ - /// MCP response metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// The user's response: accept (submitted), decline (rejected), or cancel (dismissed). - [JsonPropertyName("action")] - public UIElicitationResponseAction Action { get; set; } - /// The form values submitted by the user (present when action is 'accept'). - [JsonPropertyName("content")] - public IDictionary? Content { get; set; } + /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. + [JsonPropertyName("source")] + public PermissionsSetApproveAllSource? Source { get; set; } } -/// JSON Schema describing the form fields to present to the user. +/// Indicates whether the requested permission mode was applied and reports the authoritative post-mutation mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIElicitationSchema +public sealed class PermissionsSetModeResult { - /// Form field definitions, keyed by field name. - [JsonPropertyName("properties")] - public IDictionary Properties { get => field ??= new Dictionary(); set; } - - /// List of required field names. - [JsonPropertyName("required")] - public IList? Required { get; set; } - - /// Schema type indicator (always 'object'). - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; -} + /// Authoritative permission mode after the mutation. + [JsonPropertyName("mode")] + public PermissionMode Mode { get; set; } -/// Metadata controlling an MCP task's lifetime. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpTaskMetadata -{ - /// Task time-to-live. - [JsonPropertyName("ttl")] - public long? Ttl { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Prompt message and JSON schema describing the form fields to elicit from the user. +/// Permission mode to apply for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIElicitationRequest +internal sealed class PermissionsSetModeRequest { - /// MCP request metadata. - [JsonPropertyName("_meta")] - public IDictionary? Meta { get; set; } - - /// Message describing what information is needed from the user. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// 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; } - /// Elicitation mode. Omitted and form are equivalent for structured elicitation. + /// Permission mode to apply. [JsonPropertyName("mode")] - public McpElicitationFormMode? Mode { get; set; } - - /// JSON Schema describing the form fields to present to the user. - [JsonPropertyName("requestedSchema")] - public UIElicitationSchema RequestedSchema { get => field ??= new(); set; } + public PermissionMode Mode { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// MCP task metadata. - [JsonPropertyName("task")] - public McpTaskMetadata? Task { 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; } } -/// Indicates whether the elicitation response was accepted; false if it was already resolved by another client. +/// Current permission mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIElicitationResult +public sealed class PermissionsGetModeResult { - /// Whether the response was accepted. False if the request was already resolved by another client. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Current permission mode. + [JsonPropertyName("mode")] + public PermissionMode Mode { get; set; } } -/// Pending elicitation request ID and the user's response (accept/decline/cancel + form values). +/// No parameters. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingElicitationRequest +internal sealed class PermissionsGetModeRequest { - /// 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; } - /// 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 PermissionsModifyRulesResult { - /// 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. +/// Scope and add/remove instructions for modifying session- or location-scoped permission rules. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIUserInputResponse +internal sealed class PermissionsModifyRulesParams { - /// The user's answer text. - [JsonPropertyName("answer")] - public string Answer { get; set; } = string.Empty; + /// Rules to add to the scope. Applied before `remove`/`removeAll`. + [JsonPropertyName("add")] + public IList? Add { 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; } -} + /// Specific rules to remove from the scope. Ignored when `removeAll` is true. + [JsonPropertyName("remove")] + public IList? Remove { get; set; } -/// Request ID of a pending `user_input.requested` event and the user's response. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingUserInputRequest -{ - /// The unique request ID from the user_input.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// 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; } - /// 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 the change applies to ephemeral session-scoped rules (cleared at session end) or to location-scoped rules persisted via the location-permissions config file. + [JsonPropertyName("scope")] + public PermissionsModifyRulesScope Scope { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIHandlePendingSamplingResponse -{ -} - -/// Request ID of a pending `sampling.requested` event and an optional sampling result payload (omit to reject). +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingSamplingRequest +public sealed class PermissionsSetRequiredResult { - /// The unique request ID from the sampling.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. - [JsonPropertyName("response")] - public UIHandlePendingSamplingResponse? Response { get; set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Request ID of a pending `auto_mode_switch.requested` event and the user's response. +/// Toggles whether permission prompts should be bridged into session events for this client. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingAutoModeSwitchRequest +internal sealed class PermissionsSetRequiredRequest { - /// The unique request ID from the auto_mode_switch.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// User's choice for auto-mode switching: yes (allow this turn), yes_always (allow + persist as setting), or no (decline). - [JsonPropertyName("response")] - public UIAutoModeSwitchResponse Response { get; set; } + /// Whether the client wants `permission.requested` events bridged from the session-owned permission service. CLI clients that render prompt UI set this to `true` for as long as their listener is mounted; headless callers leave it unset (the default is `false`). + [JsonPropertyName("required")] + public bool Required { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// The user's selected action for an exhausted session limit. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UISessionLimitsExhaustedResponse +public sealed class PermissionsResetSessionApprovalsResult { - /// Action selected by the user. - [JsonPropertyName("action")] - public UISessionLimitsExhaustedResponseAction Action { get; set; } - - /// AI Credits to add to the current max when action is 'add'. - [JsonPropertyName("additionalAiCredits")] - public double? AdditionalAiCredits { get; set; } - - /// New absolute max AI Credits when action is 'set'. - [JsonPropertyName("maxAiCredits")] - public double? MaxAiCredits { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Request ID of a pending `session_limits_exhausted.requested` event and the user's selected limit action. +/// Clears session-scoped tool approvals and optionally clears location-scoped approvals and exact session-approved paths. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingSessionLimitsExhaustedRequest +internal sealed class PermissionsResetSessionApprovalsRequest { - /// The unique request ID from the session_limits_exhausted.requested event. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; - - /// The selected session-limit action. - [JsonPropertyName("response")] - public UISessionLimitsExhaustedResponse Response { get => field ??= new(); set; } + /// Whether location-scoped approvals and exact session-approved paths are cleared too. Defaults to `true`. + [JsonPropertyName("includeLocation")] + public bool? IncludeLocation { get; set; } /// Target session identifier. [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. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIExitPlanModeResponse +public sealed class PermissionsNotifyPromptShownResult { - /// Whether the plan was approved. - [JsonPropertyName("approved")] - public bool Approved { get; set; } - - /// Whether subsequent edits should be auto-approved without confirmation. - [JsonPropertyName("autoApproveEdits")] - public bool? AutoApproveEdits { get; set; } - - /// When true, the agent is instructed to end its turn without starting implementation so the client can restore the session model and auto-submit a fresh implementation turn on it. Set only when a distinct plan configuration (a different model, reasoning effort, or context tier) actually ran the planning turn. - [JsonPropertyName("deferImplementation")] - public bool? DeferImplementation { get; set; } - - /// Feedback from the user when they declined the plan or requested changes. - [JsonPropertyName("feedback")] - public string? Feedback { get; set; } - - /// The action the user selected. Defaults to 'autopilot' when autoApproveEdits is true, otherwise 'interactive'. - [JsonPropertyName("selectedAction")] - public UIExitPlanModeAction? SelectedAction { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Request ID of a pending `exit_plan_mode.requested` event and the user's response. +/// Notification payload describing the permission prompt that the client just rendered. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIHandlePendingExitPlanModeRequest +internal sealed class PermissionPromptShownNotification { - /// 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; } + /// 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; } -/// 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). +/// Snapshot of the session's recursive directory grants, exact session-approved paths, and primary working directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIRegisterDirectAutoModeSwitchHandlerResult +public sealed class PermissionPathsList { - /// 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; + /// All directories currently allowed for tool access on this session. + [JsonPropertyName("directories")] + public IList Directories { get => field ??= []; set; } + + /// The primary working directory for this session. + [JsonPropertyName("primary")] + public string Primary { get; set; } = string.Empty; + + /// Exact paths approved for this session without recursively allowing their descendants. + [JsonPropertyName("sessionApprovedPaths")] + public IList? SessionApprovedPaths { get; set; } } -/// Identifies the target session. +/// No parameters; returns the session's recursive directory grants and exact session-approved paths. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionUiRegisterDirectAutoModeSwitchHandlerRequest +internal sealed class PermissionsPathsListRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the handle was active and the registration count was decremented. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UIUnregisterDirectAutoModeSwitchHandlerResult +public sealed class PermissionsPathsAddResult { - /// True if the handle was active and decremented the counter; false if the handle was unknown. - [JsonPropertyName("unregistered")] - public bool Unregistered { get; set; } + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Opaque handle previously returned by `registerDirectAutoModeSwitchHandler` to release. +/// Directory path to add to the session's allowed directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class UIUnregisterDirectAutoModeSwitchHandlerRequest +internal sealed class PermissionPathsAddParams { - /// Handle previously returned by `registerDirectAutoModeSwitchHandler`. - [JsonPropertyName("handle")] - public string Handle { get; set; } = string.Empty; + /// 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")] @@ -20583,216 +23581,165 @@ internal sealed class UIUnregisterDirectAutoModeSwitchHandlerRequest /// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureResult +public sealed class PermissionsPathsUpdatePrimaryResult { /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { get; set; } } -/// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. +/// Directory path to set as the session's new primary working directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRuleSource +internal sealed class PermissionPathsUpdatePrimaryParams { - /// Name of the policy source. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Directory to set as the new primary working directory for the session's permission policy. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Type of the policy source. - [JsonPropertyName("type")] - public string Type { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Single content-exclusion rule supplied to `session.permissions.configure`, with paths, match conditions, and source. +/// Indicates whether the supplied path is within the session's allowed directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureAdditionalContentExclusionPolicyRule +public sealed class PermissionPathsAllowedCheckResult { - /// Conditions of which at least one must match. - [JsonPropertyName("ifAnyMatch")] - public IList? IfAnyMatch { get; set; } - - /// Conditions none of which may match. - [JsonPropertyName("ifNoneMatch")] - public IList? IfNoneMatch { get; set; } - - /// Path patterns covered by this rule. - [JsonPropertyName("paths")] - public IList Paths { get => field ??= []; set; } - - /// Source descriptor for a `session.permissions.configure` content-exclusion rule, with source name and type. - [JsonPropertyName("source")] - public PermissionsConfigureAdditionalContentExclusionPolicyRuleSource Source { get => field ??= new(); set; } + /// Whether the path is within the session's allowed directories. + [JsonPropertyName("allowed")] + public bool Allowed { get; set; } } -/// Content-exclusion policy supplied to `session.permissions.configure`, with rules, last-updated data, and scope. +/// Path to evaluate against the session's allowed directories. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsConfigureAdditionalContentExclusionPolicy +internal sealed class PermissionPathsAllowedCheckParams { - /// 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; } + /// Path to check against the session's allowed directories. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Allowed values for the `PermissionsConfigureAdditionalContentExclusionPolicyScope` enumeration. - [JsonPropertyName("scope")] - public PermissionsConfigureAdditionalContentExclusionPolicyScope Scope { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// If specified, replaces the session's path-permission policy. The runtime constructs the appropriate PathManager based on these inputs (rooted at the session's working directory). Omit to leave the current path policy unchanged. +/// Indicates whether the supplied path is within the session's workspace directory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsConfig +public sealed class PermissionPathsWorkspaceCheckResult { - /// 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; } + /// Whether the path is within the session workspace directory. + [JsonPropertyName("allowed")] + public bool Allowed { 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; } +/// 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; - /// 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. +/// Resolved location-permissions key and type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionRulesSet +public sealed class PermissionLocationResolveResult { - /// Rules that auto-approve matching requests. - [JsonPropertyName("approved")] - public IList Approved { get => field ??= []; set; } + /// Location key used in the location-permissions store. + [JsonPropertyName("locationKey")] + public string LocationKey { get; set; } = string.Empty; - /// Rules that auto-deny matching requests. - [JsonPropertyName("denied")] - public IList Denied { get => field ??= []; set; } + /// Whether the location is a git repo or directory. + [JsonPropertyName("locationType")] + public PermissionLocationType LocationType { 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. +/// Working directory to resolve into a location-permissions key. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionUrlsConfig +internal sealed class PermissionLocationResolveParams { - /// Initial list of allowed URL/domain patterns. Patterns may include path components. Ignored when `unrestricted` is true. - [JsonPropertyName("initialAllowed")] - public IList? InitialAllowed { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// 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; } + /// Working directory whose permission location should be resolved. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Patch of permission policy fields to apply (omit a field to leave it unchanged). +/// Summary of persisted location permissions applied to the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsConfigureParams +public sealed class PermissionLocationApplyResult { - /// 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; } + /// Number of persisted allowed directories added to the live path manager. + [JsonPropertyName("appliedDirectoryCount")] + public long AppliedDirectoryCount { 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; } + /// Number of location-scoped rules added to the live permission service. + [JsonPropertyName("appliedRuleCount")] + public long AppliedRuleCount { 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; } + /// Location-scoped rules applied to the live permission service. + [JsonPropertyName("appliedRules")] + public IList AppliedRules { get => field ??= []; 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 a different location was applied since the previous apply call. + [JsonPropertyName("changed")] + public bool Changed { 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; } + /// 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 load persisted location permissions for. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class PermissionLocationApplyParams +{ /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - /// 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; } + /// Working directory whose persisted location permissions should be applied. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Indicates whether the permission decision was applied; false when the request was already resolved. +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionRequestResult +public sealed class PermissionsLocationsAddToolApprovalResult { - /// Whether the permission request was handled successfully. + /// Whether the operation succeeded. [JsonPropertyName("success")] public bool Success { get; set; } } -/// The client's response to the pending permission prompt. -/// Polymorphic base type discriminated by kind. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(PermissionDecisionApproveOnce), "approve-once")] -[JsonDerivedType(typeof(PermissionDecisionApproveForSession), "approve-for-session")] -[JsonDerivedType(typeof(PermissionDecisionApproveReadOnlyForSession), "approve-read-only-for-session")] -[JsonDerivedType(typeof(PermissionDecisionApproveForLocation), "approve-for-location")] -[JsonDerivedType(typeof(PermissionDecisionApprovePermanently), "approve-permanently")] -[JsonDerivedType(typeof(PermissionDecisionReject), "reject")] -[JsonDerivedType(typeof(PermissionDecisionUserNotAvailable), "user-not-available")] -[JsonDerivedType(typeof(PermissionDecisionApproved), "approved")] -[JsonDerivedType(typeof(PermissionDecisionApprovedForSession), "approved-for-session")] -[JsonDerivedType(typeof(PermissionDecisionApprovedForLocation), "approved-for-location")] -[JsonDerivedType(typeof(PermissionDecisionCancelled), "cancelled")] -[JsonDerivedType(typeof(PermissionDecisionDeniedByRules), "denied-by-rules")] -[JsonDerivedType(typeof(PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser), "denied-no-approval-rule-and-could-not-request-from-user")] -[JsonDerivedType(typeof(PermissionDecisionDeniedInteractivelyByUser), "denied-interactively-by-user")] -[JsonDerivedType(typeof(PermissionDecisionDeniedByContentExclusionPolicy), "denied-by-content-exclusion-policy")] -[JsonDerivedType(typeof(PermissionDecisionDeniedByPermissionRequestHook), "denied-by-permission-request-hook")] -public partial class PermissionDecision -{ - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} - - -/// Permission-decision request variant to approve only the current permission request. -/// The approve-once variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveOnce : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "approve-once"; - - /// True only when a host surfaced this request to a user who approved it. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("approvedInteractively")] - public bool? ApprovedInteractively { get; set; } -} - -/// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). +/// 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(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 +[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")] @@ -20800,10 +23747,10 @@ public partial class PermissionDecisionApproveForSessionApproval } -/// Session-scoped approval details for specific command identifiers. -/// The commands variant of . +/// Location-persisted tool approval details for specific command identifiers. +/// The commands variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalCommands : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsCommands : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -20814,30 +23761,30 @@ public partial class PermissionDecisionApproveForSessionApprovalCommands : Permi public required IList CommandIdentifiers { get; set; } } -/// Session-scoped approval details for read-only filesystem operations. -/// The read variant of . +/// Location-persisted tool approval details for read-only filesystem operations. +/// The read variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalRead : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsRead : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] public override string Kind => "read"; } -/// Session-scoped approval details for filesystem write operations. -/// The write variant of . +/// Location-persisted tool approval details for filesystem write operations. +/// The write variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalWrite : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsWrite : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] public override string Kind => "write"; } -/// Session-scoped approval details for an MCP server tool, or all tools on the server when `toolName` is null. -/// The mcp variant of . +/// Location-persisted tool approval details for an MCP server tool, or all tools when `toolName` is null. +/// The mcp variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalMcp : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsMcp : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -20852,10 +23799,10 @@ public partial class PermissionDecisionApproveForSessionApprovalMcp : Permission public string? ToolName { get; set; } } -/// Session-scoped approval details for MCP sampling requests from a server. -/// The mcp-sampling variant of . +/// Location-persisted tool approval details for MCP sampling requests from a server. +/// The mcp-sampling variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalMcpSampling : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsMcpSampling : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -20866,20 +23813,20 @@ public partial class PermissionDecisionApproveForSessionApprovalMcpSampling : Pe public required string ServerName { get; set; } } -/// Session-scoped approval details for writes to long-term memory. -/// The memory variant of . +/// Location-persisted tool approval details for writes to long-term memory. +/// The memory variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalMemory : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsMemory : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] public override string Kind => "memory"; } -/// Session-scoped approval details for a custom tool, keyed by tool name. -/// The custom-tool variant of . +/// Location-persisted tool approval details for a custom tool, keyed by tool name. +/// The custom-tool variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalCustomTool : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsCustomTool : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -20890,10 +23837,10 @@ public partial class PermissionDecisionApproveForSessionApprovalCustomTool : Per public required string ToolName { get; set; } } -/// Session-scoped approval details for extension-management operations, optionally narrowed by operation. -/// The extension-management variant of . +/// Location-persisted tool approval details for extension-management operations, optionally narrowed by operation. +/// The extension-management variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalExtensionManagement : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsExtensionManagement : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -20905,10 +23852,10 @@ public partial class PermissionDecisionApproveForSessionApprovalExtensionManagem public string? Operation { get; set; } } -/// Session-scoped workflow approval, optionally narrowed by approval key. -/// The workflow variant of . +/// Location-persisted workflow approval, optionally narrowed by approval key. +/// The workflow variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalWorkflow : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsWorkflow : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -20920,10 +23867,10 @@ public partial class PermissionDecisionApproveForSessionApprovalWorkflow : Permi public string? ApprovalKey { get; set; } } -/// Session-scoped approval details for an extension's permission-gated capability access, keyed by extension name. -/// The extension-permission-access variant of . +/// Location-persisted tool approval details for an extension's permission-gated capability access, keyed by extension name. +/// The extension-permission-access variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -20934,10 +23881,10 @@ public partial class PermissionDecisionApproveForSessionApprovalExtensionPermiss public required string ExtensionName { get; set; } } -/// Session-scoped approval details for an extension's access to sensitive environment variables, keyed by extension name and the exact set of variable names. -/// The extension-env-access variant of . +/// Location-persisted tool approval details for an extension's access to sensitive environment variables, keyed by extension name and the exact set of variable names. +/// The extension-env-access variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSessionApprovalExtensionEnvAccess : PermissionDecisionApproveForSessionApproval +public partial class PermissionsLocationsAddToolApprovalDetailsExtensionEnvAccess : PermissionsLocationsAddToolApprovalDetails { /// [JsonIgnore] @@ -20952,3286 +23899,3773 @@ public partial class PermissionDecisionApproveForSessionApprovalExtensionEnvAcce public required string ExtensionName { 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 . +/// Location-scoped tool approval to persist. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForSession : PermissionDecision +internal sealed class PermissionLocationAddToolApprovalParams { - /// - [JsonIgnore] - public override string Kind => "approve-for-session"; - - /// Session-scoped approval to remember (tool prompts only; omitted for path/url prompts). - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// Tool approval to persist and apply. [JsonPropertyName("approval")] - public PermissionDecisionApproveForSessionApproval? Approval { get; set; } + public PermissionsLocationsAddToolApprovalDetails Approval { 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; } + /// 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; } -/// Approve file-tool read access to specific directories for this logical session, including continuation or resume. -/// The approve-read-only-for-session variant of . +/// Folder trust check result. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveReadOnlyForSession : PermissionDecision +public sealed class FolderTrustCheckResult { - /// - [JsonIgnore] - public override string Kind => "approve-read-only-for-session"; - - /// Canonical directories covered by the read-only grant. - [JsonPropertyName("directories")] - public required IList Directories { get; set; } + /// Whether the folder is trusted. + [JsonPropertyName("trusted")] + public bool Trusted { get; set; } } -/// Approval to persist for this location. -/// Polymorphic base type discriminated by kind. +/// Folder path to check for trust. [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 +internal sealed class FolderTrustCheckParams { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; + /// 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. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class PermissionsFolderTrustAddTrustedResult +{ + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } +} -/// Location-scoped approval details for specific command identifiers. -/// The commands variant of . +/// Folder path to add to trusted folders. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalCommands : PermissionDecisionApproveForLocationApproval +internal sealed class FolderTrustAddParams { - /// - [JsonIgnore] - public override string Kind => "commands"; + /// Folder path to mark as trusted. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// 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-scoped approval details for read-only filesystem operations. -/// The read variant of . +/// Indicates whether the operation succeeded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalRead : PermissionDecisionApproveForLocationApproval +public sealed class PermissionsUrlsSetUnrestrictedModeResult { - /// - [JsonIgnore] - public override string Kind => "read"; + /// Whether the operation succeeded. + [JsonPropertyName("success")] + public bool Success { get; set; } } -/// Location-scoped approval details for filesystem write operations. -/// The write variant of . +/// Whether the URL-permission policy should run in unrestricted mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalWrite : PermissionDecisionApproveForLocationApproval +internal sealed class PermissionUrlsSetUnrestrictedModeParams { - /// - [JsonIgnore] - public override string Kind => "write"; + /// 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; } -/// Location-scoped approval details for an MCP server tool, or all tools on the server when `toolName` is null. -/// The mcp variant of . +/// The repository the remote session targets. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalMcp : PermissionDecisionApproveForLocationApproval +public sealed class MetadataSnapshotRemoteMetadataRepository { - /// - [JsonIgnore] - public override string Kind => "mcp"; + /// The branch the remote session is operating on. + [JsonPropertyName("branch")] + public string Branch { get; set; } = string.Empty; - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// The GitHub repository name (without owner). + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// MCP tool name, or null to cover every tool on the server. - [JsonPropertyName("toolName")] - public string? ToolName { get; set; } + /// The GitHub owner (user or organization) of the target repository. + [JsonPropertyName("owner")] + public string Owner { get; set; } = string.Empty; } -/// Location-scoped approval details for MCP sampling requests from a server. -/// The mcp-sampling variant of . +/// Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalMcpSampling : PermissionDecisionApproveForLocationApproval +public sealed class MetadataSnapshotRemoteMetadata { - /// - [JsonIgnore] - public override string Kind => "mcp-sampling"; + /// The pull request number the remote session is associated with, if any. + [JsonPropertyName("pullRequestNumber")] + public long? PullRequestNumber { get; set; } - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// The repository the remote session targets. + [JsonPropertyName("repository")] + public MetadataSnapshotRemoteMetadataRepository Repository { get => field ??= new(); set; } + + /// The original resource identifier (task ID or PR node ID), preserved across event-replay reconstructions. Falls back to `sessionId` when absent. + [JsonPropertyName("resourceId")] + public string? ResourceId { get; set; } + + /// Whether the remote task originated from Copilot Coding Agent (cca) or a CLI `--remote` invocation. + [JsonPropertyName("taskType")] + public MetadataSnapshotRemoteMetadataTaskType? TaskType { get; set; } } -/// Location-scoped approval details for writes to long-term memory. -/// The memory variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalMemory : PermissionDecisionApproveForLocationApproval -{ - /// - [JsonIgnore] - public override string Kind => "memory"; -} +/// Public-facing projection of workspace metadata for SDK / TUI consumers. +public sealed class SessionMetadataSnapshotWorkspace +{ + /// Branch checked out at session start, if any. + [JsonPropertyName("branch")] + public string? Branch { get; set; } + + /// ISO 8601 timestamp when the workspace was created. + [JsonPropertyName("created_at")] + public DateTimeOffset? CreatedAt { get; set; } + + /// 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; } -/// Location-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 PermissionDecisionApproveForLocationApprovalCustomTool : PermissionDecisionApproveForLocationApproval -{ - /// - [JsonIgnore] - public override string Kind => "custom-tool"; + /// ISO 8601 timestamp when the workspace was last updated. + [JsonPropertyName("updated_at")] + public DateTimeOffset? UpdatedAt { get; set; } - /// Custom tool name. - [JsonPropertyName("toolName")] - public required string ToolName { get; set; } + /// Whether the display name was explicitly set by the user. + [JsonPropertyName("user_named")] + public bool? UserNamed { get; set; } } -/// Location-scoped approval details for extension-management operations, optionally narrowed by operation. -/// The extension-management variant of . +/// Point-in-time snapshot of slow-changing session identifier and state fields. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalExtensionManagement : PermissionDecisionApproveForLocationApproval +public sealed class SessionMetadataSnapshot { - /// - [JsonIgnore] - public override string Kind => "extension-management"; + /// 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; } - /// Optional operation identifier; when omitted, the approval covers all extension management operations. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("operation")] - public string? Operation { get; set; } -} + /// Runtime client name associated with the session (telemetry identifier). + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } -/// Location-scoped workflow approval, optionally narrowed by approval key. -/// The workflow variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocationApprovalWorkflow : PermissionDecisionApproveForLocationApproval -{ - /// - [JsonIgnore] - public override string Kind => "workflow"; + /// The current agent mode for this session (e.g., 'interactive', 'plan', 'autopilot'). + [JsonPropertyName("currentMode")] + public MetadataSnapshotCurrentMode CurrentMode { 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; } -} + /// 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; } -/// 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 partial class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess : PermissionDecisionApproveForLocationApproval -{ - /// - [JsonIgnore] - public override string Kind => "extension-permission-access"; + /// User-provided name supplied at session construction (via `--name`), if any. Immutable after construction. + [JsonPropertyName("initialName")] + public string? InitialName { get; set; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { 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; } -/// 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 partial class PermissionDecisionApproveForLocationApprovalExtensionEnvAccess : PermissionDecisionApproveForLocationApproval -{ - /// - [JsonIgnore] - public override string Kind => "extension-env-access"; + /// 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; } - /// Names of the sensitive environment variables this approval covers. Values are never persisted. - [JsonPropertyName("environmentVariables")] - public required IList EnvironmentVariables { 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; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } -} + /// Currently selected model identifier, if any. + [JsonPropertyName("selectedModel")] + public string? SelectedModel { 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 . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproveForLocation : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "approve-for-location"; + /// The unique identifier of the session. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Approval to persist for this location. - [JsonPropertyName("approval")] - public required PermissionDecisionApproveForLocationApproval Approval { get; set; } + /// Current session limits, or null when no limits are active. + [JsonPropertyName("sessionLimits")] + public SessionLimitsConfig? SessionLimits { get; set; } - /// Location key (git root or cwd) to persist the approval to. - [JsonPropertyName("locationKey")] - public required string LocationKey { get; set; } -} + /// ISO 8601 timestamp of when the session started. + [JsonPropertyName("startTime")] + public DateTimeOffset StartTime { get; set; } -/// Permission-decision request variant to permanently approve a URL domain across sessions. -/// The approve-permanently variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApprovePermanently : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "approve-permanently"; + /// Short human-readable summary of the session, if known. Omitted when no summary has been generated. + [JsonPropertyName("summary")] + public string? Summary { get; set; } - /// URL domain to approve permanently. - [JsonPropertyName("domain")] - public required string Domain { get; set; } -} + /// Absolute path to the session's current working directory. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; -/// Permission-decision request variant to reject a pending permission request, with optional feedback. -/// The reject variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionReject : PermissionDecision -{ - /// - [JsonIgnore] - public override string Kind => "reject"; + /// 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 feedback explaining the rejection. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("feedback")] - public string? Feedback { 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; } } -/// Permission-decision variant indicating no user was available to confirm the request. -/// The user-not-available variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionUserNotAvailable : PermissionDecision +internal sealed class SessionMetadataSnapshotRequest { - /// - [JsonIgnore] - public override string Kind => "user-not-available"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Permission-decision variant indicating the request was approved. -/// The approved variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApproved : PermissionDecision +internal sealed class SessionMetadataGetClientMetadataRequest { - /// - [JsonIgnore] - public override string Kind => "approved"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Permission-decision variant indicating approval was remembered for the session, with approval details. -/// The approved-for-session variant of . +/// 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 partial class PermissionDecisionApprovedForSession : PermissionDecision +internal sealed class MetadataUpdateClientMetadataRequest { - /// - [JsonIgnore] - public override string Kind => "approved-for-session"; + /// Remove every existing client metadata entry before applying remove and set. Defaults to false. + [JsonPropertyName("clear")] + public bool? Clear { get; set; } - /// The approval to add as a session-scoped rule. - [JsonPropertyName("approval")] - public required UserToolSessionApproval Approval { get; set; } + /// Case-sensitive keys to remove. Missing keys are ignored. Each key must be non-empty, at most 256 UTF-8 bytes, and outside the reserved `copilot/` and `github/` namespaces. + [JsonPropertyName("remove")] + public IList? Remove { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// 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; } } -/// Permission-decision variant indicating approval was persisted for a project location, with approval details and location key. -/// The approved-for-location variant of . +/// Indicates whether the local session is currently processing a turn or background continuation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionApprovedForLocation : PermissionDecision +public sealed class MetadataIsProcessingResult { - /// - [JsonIgnore] - public override string Kind => "approved-for-location"; - - /// The approval to persist for this location. - [JsonPropertyName("approval")] - public required UserToolSessionApproval Approval { get; set; } - - /// The location key (git root or cwd) to persist the approval to. - [JsonPropertyName("locationKey")] - public required string LocationKey { get; set; } + /// Whether the session is currently processing user/agent messages. False for non-local sessions (which don't run a local agentic loop). Reflects an in-flight turn or background continuation. + [JsonPropertyName("processing")] + public bool Processing { get; set; } } -/// Permission-decision variant indicating the request was cancelled before use, with an optional reason. -/// The cancelled variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionCancelled : PermissionDecision +internal sealed class SessionMetadataIsProcessingRequest { - /// - [JsonIgnore] - public override string Kind => "cancelled"; - - /// Optional explanation of why the request was cancelled. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("reason")] - public string? Reason { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Permission-decision variant indicating explicit denial by permission rules, with the matching rules. -/// The denied-by-rules variant of . +/// Current activity flags for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionDeniedByRules : PermissionDecision +public sealed class SessionActivity { - /// - [JsonIgnore] - public override string Kind => "denied-by-rules"; + /// Whether an in-flight operation can currently be aborted. + [JsonPropertyName("abortable")] + public bool Abortable { get; set; } - /// Rules that denied the request. - [JsonPropertyName("rules")] - public required IList Rules { get; set; } + /// Whether the session currently has active work, including running turns or tasks. + [JsonPropertyName("hasActiveWork")] + public bool HasActiveWork { 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 . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser : PermissionDecision +internal sealed class SessionMetadataActivityRequest { - /// - [JsonIgnore] - public override string Kind => "denied-no-approval-rule-and-could-not-request-from-user"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// 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 +/// Token-usage breakdown for the session's current context window. +public sealed class MetadataContextInfoResultContextInfo { - /// - [JsonIgnore] - public override string Kind => "denied-interactively-by-user"; + /// 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; } - /// Optional feedback from the user explaining the denial. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("feedback")] - public string? Feedback { get; set; } + /// Token count at which background compaction starts (configurable percentage of promptTokenLimit). + [JsonPropertyName("compactionThreshold")] + public long CompactionThreshold { get; set; } - /// Whether to force-reject the current agent turn. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("forceReject")] - public bool? ForceReject { get; set; } -} + /// Tokens consumed by user/assistant/tool messages. + [JsonPropertyName("conversationTokens")] + public long ConversationTokens { 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"; + /// Provider-owned model display label, or the Auto routing label. Presentation only; modelName remains the tokenization identity. + [JsonPropertyName("displayModelName")] + public string? DisplayModelName { get; set; } - /// Human-readable explanation of why the path was excluded. - [JsonPropertyName("message")] - public required string Message { 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; } - /// File path that triggered the exclusion. - [JsonPropertyName("path")] - public required string Path { get; set; } + /// Tokens consumed by MCP tool definitions (subset of toolDefinitionsTokens, excludes deferred tools). + [JsonPropertyName("mcpToolsTokens")] + public long McpToolsTokens { get; set; } + + /// The model used for token counting. + [JsonPropertyName("modelName")] + public string ModelName { get; set; } = string.Empty; + + /// Effective input budget: the selected tier's prompt allowance bounded by the combined context ceiling minus the requested output allowance. Uses DEFAULT_TOKEN_LIMIT when limits are unspecified. + [JsonPropertyName("promptTokenLimit")] + public long PromptTokenLimit { get; set; } + + /// Provider whose catalog supplies this context window. Omitted when provider identity is unavailable. + [JsonPropertyName("provider")] + public ModelProviderRef? Provider { get; set; } + + /// Tokens consumed by the system prompt. + [JsonPropertyName("systemTokens")] + public long SystemTokens { get; set; } + + /// Tokens consumed by tool definitions sent to the model (excludes deferred tools). + [JsonPropertyName("toolDefinitionsTokens")] + public long ToolDefinitionsTokens { get; set; } + + /// Sum of system, conversation and tool-definition tokens. + [JsonPropertyName("totalTokens")] + public long TotalTokens { get; set; } } -/// Permission-decision variant indicating denial by a permission request hook, with optional message and interrupt flag. -/// The denied-by-permission-request-hook variant of . +/// Token breakdown for the session's current context window, or null if uninitialized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionDecisionDeniedByPermissionRequestHook : PermissionDecision +public sealed class MetadataContextInfoResult { - /// - [JsonIgnore] - public override string Kind => "denied-by-permission-request-hook"; - - /// Whether to interrupt the current agent turn. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("interrupt")] - public bool? Interrupt { get; set; } - - /// Optional message from the hook explaining the denial. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("message")] - public string? Message { get; set; } + /// 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; } } -/// Pending permission request ID and the decision to apply (approve/reject and scope). +/// Model identifier and token limits used to compute the context-info breakdown. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionDecisionRequest +internal sealed class MetadataContextInfoRequest { - /// 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; } + /// 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; } - /// Request ID of the pending permission request. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; + /// Advertised prompt allowance. Pass 0 to resolve the selected model and context tier from the session. + [JsonPropertyName("promptTokenLimit")] + public long PromptTokenLimit { get; set; } - /// The client's response to the pending permission prompt. - [JsonPropertyName("result")] - public PermissionDecision Result { get => field ??= new(); 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; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Pending permission prompt reconstructed from event history, with request ID and user-facing prompt details. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PendingPermissionRequest +/// 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 { - /// Permission-recovery episode that authorized this request to surface for interactive attention. - [JsonPropertyName("recoveryEpisodeId")] - public string? RecoveryEpisodeId { get; set; } + /// Overlapping output reservation plus post-blocking-threshold buffer. + [JsonPropertyName("buffer")] + public long Buffer { 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!; + /// Custom-instructions tokens (0 when none are configured). + [JsonPropertyName("customInstructions")] + public long CustomInstructions { get; set; } - /// Unique identifier for the pending permission request. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; -} + /// Remaining unused window capacity (clamped at 0). + [JsonPropertyName("freeSpace")] + public long FreeSpace { 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; } -} + /// MCP tool-definition tokens. + [JsonPropertyName("mcpTools")] + public long McpTools { 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; + /// Conversation (user/assistant/tool) message tokens. + [JsonPropertyName("messages")] + public long Messages { get; set; } + + /// System prompt tokens, excluding custom instructions. + [JsonPropertyName("systemPrompt")] + public long SystemPrompt { get; set; } + + /// Non-MCP tool-definition tokens. + [JsonPropertyName("systemTools")] + public long SystemTools { get; set; } } -/// Indicates whether the operation succeeded. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsSetApproveAllResult +/// Successful compaction history for the session. +public sealed class MetadataContextAttributionResultContextAttributionCompactions { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Number of successful compactions in this session. + [JsonPropertyName("count")] + public long Count { get; set; } } -/// Allow-all toggle for tool permission requests, with an optional telemetry source. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsSetApproveAllRequest +/// RPC data type for MetadataContextAttributionResultContextAttributionEntry operations. +public sealed class MetadataContextAttributionResultContextAttributionEntry { - /// Whether to auto-approve all tool permission requests. - [JsonPropertyName("enabled")] - public bool Enabled { 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; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; - /// Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers. - [JsonPropertyName("source")] - public PermissionsSetApproveAllSource? Source { 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; + + /// Human-readable display label, e.g. `bash` or `skill: tmux`. Presentation-only; may be localized/reformatted without notice — do not key off it. + [JsonPropertyName("label")] + public string Label { get; set; } = string.Empty; + + /// Optional `id` of the parent entry: e.g. a `plugin` entry parenting its `skill`/`mcpServer` entries, or the `system` entry parenting `toolDefinition` entries. Omitted for top-level entries. + [JsonPropertyName("parentId")] + public string? ParentId { get; set; } + + /// Token count currently in context attributable to this entry. + [JsonPropertyName("tokens")] + public long Tokens { get; set; } } -/// Indicates whether the requested permission mode was applied and reports the authoritative post-mutation mode. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsSetModeResult +/// Per-source token attribution snapshot for the current context window. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. +public sealed class MetadataContextAttributionResultContextAttribution { - /// Authoritative permission mode after the mutation. - [JsonPropertyName("mode")] - public PermissionMode Mode { get; set; } + /// Output reservation overlapping the displayed prompt allowance plus the tokens past the effective input budget's buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. + [JsonPropertyName("bufferTokens")] + public long BufferTokens { get; set; } + + /// The six normalized `/context` header buckets, computed from the same tokenization as `entries` so the two never disagree. Convenience rollups: `freeSpace` and `buffer` describe window capacity rather than occupied context, so the values do not sum to `totalTokens`. + [JsonPropertyName("categories")] + public MetadataContextAttributionResultContextAttributionCategories Categories { get => field ??= new(); set; } + + /// Successful compaction history for the session. + [JsonPropertyName("compactions")] + public MetadataContextAttributionResultContextAttributionCompactions Compactions { get => field ??= new(); set; } + + /// Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. + [JsonPropertyName("compactionThreshold")] + public long CompactionThreshold { get; set; } + + /// Flat list of per-source attribution entries. Group by `kind` and render unrecognized kinds generically. Nesting and rollups are expressed via `parentId`. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } -} + /// Advertised prompt allowance for the selected context tier: the denominator for context-usage displays and capacity for `categories.freeSpace` and `categories.buffer`. Mirrors `SessionContextInfo.limit`. + [JsonPropertyName("limit")] + public long Limit { get; set; } -/// Permission mode to apply for the session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsSetModeRequest -{ - /// 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; } + /// 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; - /// Permission mode to apply. - [JsonPropertyName("mode")] - public PermissionMode Mode { 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; } - /// 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; } + /// 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; } } -/// Current permission mode. +/// Per-source attribution breakdown for the session's current context window, or null if uninitialized. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsGetModeResult +public sealed class MetadataContextAttributionResult { - /// Current permission mode. - [JsonPropertyName("mode")] - public PermissionMode Mode { get; set; } + /// Per-source context-window attribution, or null if the session has not yet been initialized (no system prompt or tool metadata cached). + [JsonPropertyName("contextAttribution")] + public MetadataContextAttributionResultContextAttribution? ContextAttribution { get; set; } } -/// No parameters. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsGetModeRequest +internal sealed class SessionMetadataGetContextAttributionRequest { /// 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 PermissionsModifyRulesResult -{ - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } -} - -/// Scope and add/remove instructions for modifying session- or location-scoped permission rules. +/// A single large message currently in context. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsModifyRulesParams +public sealed class ContextHeaviestMessage { - /// 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; } + /// Stable identifier for this message within the snapshot. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// 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; } + /// Human-readable source label, e.g. `tool: bash` or `skill: tmux`. Presentation-only. + [JsonPropertyName("label")] + public string Label { 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; } + /// Role of the chat message (`user`, `assistant`, or `tool`). + [JsonPropertyName("role")] + public string Role { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Token count currently in context for this individual message. + [JsonPropertyName("tokens")] + public long Tokens { get; set; } } -/// Indicates whether the operation succeeded. +/// The heaviest individual messages in the session's context window, most-expensive first. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsSetRequiredResult +public sealed class MetadataContextHeaviestMessagesResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// 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; } } -/// Toggles whether permission prompts should be bridged into session events for this client. +/// Parameters for the heaviest-messages query. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsSetRequiredRequest +internal sealed class MetadataContextHeaviestMessagesRequest { - /// 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; } + /// Maximum number of messages to return, most-expensive first. Omit for the server default. + [JsonPropertyName("limit")] + public long? Limit { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Notify the session that its working directory context has changed. Emits a `session.context_changed` event so consumers (telemetry, OTel tracker, ACP, the timeline UI) can react. Use this when the host has detected a cwd/branch/repo change outside the session's normal lifecycle (e.g., after a shell command in interactive mode). For a local session, a report whose `cwd` diverges from the session's current working directory is ignored (the call still succeeds but records nothing and emits no event); move a local session's working directory via `metadata.setWorkingDirectory` instead. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsResetSessionApprovalsResult +public sealed class MetadataRecordContextChangeResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } } -/// Clears session-scoped tool approvals and optionally clears location-scoped approvals and exact session-approved paths. +/// Updated working-directory/git context to record on the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsResetSessionApprovalsRequest +internal sealed class MetadataRecordContextChangeRequest { - /// Whether location-scoped approvals and exact session-approved paths are cleared too. Defaults to `true`. - [JsonPropertyName("includeLocation")] - public bool? IncludeLocation { get; set; } + /// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. + [JsonPropertyName("context")] + public SessionWorkingDirectoryContext Context { get => field ??= new(); set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Update the session's working directory. Used by the host when the user explicitly changes cwd (e.g., the `/cd` slash command). The host is responsible for any related side-effects (file index, etc.); it does NOT change the process working directory (a session's cwd is per-session, not process-global). For local sessions the runtime validates the target first (an absolute path that exists on disk) and re-bases the permission primary directory; a rejected validation fails the call before anything is mutated, persisted, or emitted. Location-scoped permission rules are then re-keyed to the new directory (best-effort). Remote sessions only record the path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsNotifyPromptShownResult +public sealed class MetadataSetWorkingDirectoryResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Working directory after the update. + [JsonPropertyName("workingDirectory")] + public string WorkingDirectory { get; set; } = string.Empty; } -/// Notification payload describing the permission prompt that the client just rendered. +/// Absolute path to set as the session's new working directory. For local sessions the path must be absolute and exist on disk: it is validated before any session state changes, and a failing validation rejects the call with nothing mutated, persisted, or emitted. Remote sessions record the path as-is. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPromptShownNotification +internal sealed class MetadataSetWorkingDirectoryRequest { - /// 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; + + /// 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; } -/// Snapshot of the session's recursive directory grants, exact session-approved paths, and primary working directory. +/// Re-tokenize the session's existing messages against `modelId` and return the token totals. Useful for hosts that want an initial estimate of context usage on session resume, before the next agent turn fires `session.context_info_changed` events. Returns zeros for an empty session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsList +public sealed class MetadataRecomputeContextTokensResult { - /// All directories currently allowed for tool access on this session. - [JsonPropertyName("directories")] - public IList Directories { get => field ??= []; set; } + /// Tokens contributed by user/assistant/tool messages (excludes system/developer prompts). + [JsonPropertyName("messagesTokenCount")] + public long MessagesTokenCount { get; set; } - /// The primary working directory for this session. - [JsonPropertyName("primary")] - public string Primary { get; set; } = string.Empty; + /// Tokens contributed by system/developer prompt snapshots. + [JsonPropertyName("systemTokenCount")] + public long SystemTokenCount { get; set; } - /// Exact paths approved for this session without recursively allowing their descendants. - [JsonPropertyName("sessionApprovedPaths")] - public IList? SessionApprovedPaths { get; set; } + /// Sum of tokens across chat-context and system-context messages currently held by the session. + [JsonPropertyName("totalTokens")] + public long TotalTokens { get; set; } } -/// No parameters; returns the session's recursive directory grants and exact session-approved paths. +/// Model identifier to use when re-tokenizing the session's existing messages. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionsPathsListRequest +internal sealed class MetadataRecomputeContextTokensRequest { + /// Model identifier used for tokenization. The runtime token-counts both chat-context and system-context messages against this model. + [JsonPropertyName("modelId")] + public string ModelId { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Availability of built-in job tools surfaced to boundary consumers. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsPathsAddResult +public sealed class SessionSettingsBuiltInToolAvailabilitySnapshot { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether the create-pull-request tool is available. + [JsonPropertyName("createPullRequest")] + public bool? CreatePullRequest { get; set; } + + /// Whether the report-progress tool is available. + [JsonPropertyName("reportProgress")] + public bool? ReportProgress { get; set; } } -/// Directory path to add to the session's allowed directories. +/// Redacted job settings for a session. The job nonce is excluded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsAddParams +public sealed class SessionSettingsJobSnapshot { - /// 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; + /// Availability of job-specific built-in tools. + [JsonPropertyName("builtInToolAvailability")] + public SessionSettingsBuiltInToolAvailabilitySnapshot? BuiltInToolAvailability { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// GitHub Actions event type for the job. + [JsonPropertyName("eventType")] + public string? EventType { get; set; } -/// Indicates whether the operation succeeded. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsPathsUpdatePrimaryResult -{ - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// Whether this is the workflow's trigger job. + [JsonPropertyName("isTriggerJob")] + public bool? IsTriggerJob { get; set; } } -/// Directory path to set as the session's new primary working directory. +/// Redacted model routing settings for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsUpdatePrimaryParams +public sealed class SessionSettingsModelSnapshot { - /// Directory to set as the new primary working directory for the session's permission policy. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Agent service callback URL for job and progress updates. + [JsonPropertyName("callbackUrl")] + public string? CallbackUrl { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Default reasoning effort for the selected model. + [JsonPropertyName("defaultReasoningEffort")] + public string? DefaultReasoningEffort { 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; } + /// Agent job identifier for the session. + [JsonPropertyName("instanceId")] + public string? InstanceId { get; set; } + + /// Selected model identifier. + [JsonPropertyName("model")] + public string? Model { get; set; } } -/// Path to evaluate against the session's allowed directories. +/// Online-evaluation settings safe to expose across the SDK boundary. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionPathsAllowedCheckParams +public sealed class SessionSettingsOnlineEvaluationSnapshot { - /// Path to check against the session's allowed directories. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Whether online evaluation is disabled. + [JsonPropertyName("disableOnlineEvaluation")] + public bool? DisableOnlineEvaluation { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether online-evaluation output-file generation is enabled. + [JsonPropertyName("enableOnlineEvaluationOutputFile")] + public bool? EnableOnlineEvaluationOutputFile { get; set; } } -/// Indicates whether the supplied path is within the session's workspace directory. +/// Redacted repository and GitHub host settings for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionPathsWorkspaceCheckResult +public sealed class SessionSettingsRepoSnapshot { - /// Whether the path is within the session workspace directory. - [JsonPropertyName("allowed")] - public bool Allowed { get; set; } -} + /// Checked-out repository branch. + [JsonPropertyName("branch")] + public string? Branch { 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; + /// Checked-out commit SHA. + [JsonPropertyName("commit")] + public string? Commit { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// GitHub server host name. + [JsonPropertyName("host")] + public string? Host { get; set; } -/// 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; + /// Protocol used to access the GitHub host. + [JsonPropertyName("hostProtocol")] + public string? HostProtocol { get; set; } - /// Whether the location is a git repo or directory. - [JsonPropertyName("locationType")] - public PermissionLocationType LocationType { get; set; } -} + /// GitHub repository database ID. + [JsonPropertyName("id")] + public double? Id { 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; + /// Repository name. + [JsonPropertyName("name")] + public string? Name { get; set; } - /// Working directory whose permission location should be resolved. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; + /// GitHub repository owner database ID. + [JsonPropertyName("ownerId")] + public double? OwnerId { get; set; } + + /// Repository owner login. + [JsonPropertyName("ownerName")] + public string? OwnerName { get; set; } + + /// Number of commits in the pull request. + [JsonPropertyName("prCommitCount")] + public double? PrCommitCount { get; set; } + + /// Whether the repository is writable. + [JsonPropertyName("readWrite")] + public bool? ReadWrite { get; set; } + + /// GitHub secret-scanning service URL. + [JsonPropertyName("secretScanningUrl")] + public string? SecretScanningUrl { get; set; } + + /// GitHub server base URL. + [JsonPropertyName("serverUrl")] + public string? ServerUrl { get; set; } } -/// Summary of persisted location permissions applied to the session. +/// Redacted validation and memory-tool settings for a session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionLocationApplyResult +public sealed class SessionSettingsValidationSnapshot { - /// Number of persisted allowed directories added to the live path manager. - [JsonPropertyName("appliedDirectoryCount")] - public long AppliedDirectoryCount { get; set; } + /// Whether advisory validation is enabled. + [JsonPropertyName("advisoryEnabled")] + public bool? AdvisoryEnabled { get; set; } - /// Number of location-scoped rules added to the live permission service. - [JsonPropertyName("appliedRuleCount")] - public long AppliedRuleCount { get; set; } + /// Whether CodeQL validation is enabled. + [JsonPropertyName("codeqlEnabled")] + public bool? CodeqlEnabled { get; set; } - /// Location-scoped rules applied to the live permission service. - [JsonPropertyName("appliedRules")] - public IList AppliedRules { get => field ??= []; set; } + /// Whether code-review validation is enabled. + [JsonPropertyName("codeReviewEnabled")] + public bool? CodeReviewEnabled { get; set; } - /// Whether a different location was applied since the previous apply call. - [JsonPropertyName("changed")] - public bool Changed { get; set; } + /// Model used for code-review validation. + [JsonPropertyName("codeReviewModel")] + public string? CodeReviewModel { get; set; } - /// Location key used in the location-permissions store. - [JsonPropertyName("locationKey")] - public string LocationKey { get; set; } = string.Empty; + /// Dependabot validation timeout budget in seconds. + [JsonPropertyName("dependabotTimeout")] + public double? DependabotTimeout { get; set; } - /// Whether the location is a git repo or directory. - [JsonPropertyName("locationType")] - public PermissionLocationType LocationType { get; set; } -} + /// Whether the memory-store tool is enabled. + [JsonPropertyName("memoryStoreEnabled")] + public bool? MemoryStoreEnabled { get; set; } -/// Working directory to load persisted location permissions for. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionLocationApplyParams -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether the memory-vote tool is enabled. + [JsonPropertyName("memoryVoteEnabled")] + public bool? MemoryVoteEnabled { get; set; } - /// Working directory whose persisted location permissions should be applied. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; -} + /// Whether secret-scanning validation is enabled. + [JsonPropertyName("secretScanningEnabled")] + public bool? SecretScanningEnabled { get; set; } -/// Indicates whether the operation succeeded. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsLocationsAddToolApprovalResult -{ - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// General validation timeout budget in seconds. + [JsonPropertyName("timeout")] + public double? Timeout { get; set; } } -/// Tool approval to persist and apply. -/// Polymorphic base type discriminated by kind. +/// Redacted, serializable view of session runtime settings for SDK boundary consumers. Secrets and raw feature flags are intentionally excluded. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "kind", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsCommands), "commands")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsRead), "read")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsWrite), "write")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMcp), "mcp")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMcpSampling), "mcp-sampling")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsMemory), "memory")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsCustomTool), "custom-tool")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionManagement), "extension-management")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsWorkflow), "workflow")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess), "extension-permission-access")] -[JsonDerivedType(typeof(PermissionsLocationsAddToolApprovalDetailsExtensionEnvAccess), "extension-env-access")] -public partial class PermissionsLocationsAddToolApprovalDetails +internal sealed class SessionSettingsSnapshot { - /// The type discriminator. - [JsonPropertyName("kind")] - public virtual string Kind { get; set; } = string.Empty; -} + /// Name of the SDK client that created the session. + [JsonPropertyName("clientName")] + public string? ClientName { get; set; } + + /// Redacted job settings. + [JsonPropertyName("job")] + public SessionSettingsJobSnapshot Job { get => field ??= new(); set; } + /// Redacted model routing settings. + [JsonPropertyName("model")] + public SessionSettingsModelSnapshot Model { get => field ??= new(); set; } -/// Location-persisted tool approval details for specific command identifiers. -/// The commands variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsCommands : PermissionsLocationsAddToolApprovalDetails -{ - /// - [JsonIgnore] - public override string Kind => "commands"; + /// Online-evaluation settings safe for SDK consumers. + [JsonPropertyName("onlineEvaluation")] + public SessionSettingsOnlineEvaluationSnapshot OnlineEvaluation { get => field ??= new(); set; } - /// Command identifiers covered by this approval. - [JsonPropertyName("commandIdentifiers")] - public required IList CommandIdentifiers { get; set; } + /// Redacted repository and host settings. + [JsonPropertyName("repo")] + public SessionSettingsRepoSnapshot Repo { get => field ??= new(); set; } + + /// Session start time as Unix epoch milliseconds. + [JsonPropertyName("startTimeMs")] + public double? StartTimeMs { get; set; } + + /// Session timeout in milliseconds. + [JsonPropertyName("timeoutMs")] + public double? TimeoutMs { get; set; } + + /// Redacted validation and memory-tool settings. + [JsonPropertyName("validation")] + public SessionSettingsValidationSnapshot Validation { get => field ??= new(); set; } + + /// Agent runtime version selector copied from the session settings, such as `latest` or a runtime release identifier. + [JsonPropertyName("version")] + public string? Version { get; set; } } -/// Location-persisted tool approval details for read-only filesystem operations. -/// The read variant of . +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsRead : PermissionsLocationsAddToolApprovalDetails +internal sealed class SessionSettingsSnapshotRequest { - /// - [JsonIgnore] - public override string Kind => "read"; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Location-persisted tool approval details for filesystem write operations. -/// The write variant of . +/// Result of evaluating a Rust-owned settings predicate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsWrite : PermissionsLocationsAddToolApprovalDetails +internal sealed class SessionSettingsEvaluatePredicateResult { - /// - [JsonIgnore] - public override string Kind => "write"; + /// Whether the named settings predicate evaluated to enabled. + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } } -/// Location-persisted tool approval details for an MCP server tool, or all tools when `toolName` is null. -/// The mcp variant of . +/// Named Rust-owned settings predicate to evaluate for this session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsMcp : PermissionsLocationsAddToolApprovalDetails +internal sealed class SessionSettingsEvaluatePredicateRequest { - /// - [JsonIgnore] - public override string Kind => "mcp"; + /// Predicate name. The runtime owns the raw feature-flag names and composition logic. + [JsonPropertyName("name")] + public SessionSettingsPredicateName Name { get; set; } - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// MCP tool name, or null to cover every tool on the server. + /// Tool name for tool-scoped predicates such as trivial-change handling. [JsonPropertyName("toolName")] public string? ToolName { get; set; } } -/// Location-persisted tool approval details for MCP sampling requests from a server. -/// The mcp-sampling variant of . +/// Content-exclusion decision for one requested path. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsMcpSampling : PermissionsLocationsAddToolApprovalDetails +public sealed class ContentExclusionPathCheck { - /// - [JsonIgnore] - public override string Kind => "mcp-sampling"; + /// Whether the session's complete content-exclusion policy excludes the path. + [JsonPropertyName("excluded")] + public bool Excluded { get; set; } - /// MCP server name. - [JsonPropertyName("serverName")] - public required string ServerName { get; set; } + /// The path supplied by the caller. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; } -/// Location-persisted tool approval details for writes to long-term memory. -/// The memory variant of . +/// Batch content-exclusion result. Callers must fail closed when policy evaluation is unavailable. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsMemory : PermissionsLocationsAddToolApprovalDetails +public sealed class ContentExclusionCheckPathsResult { - /// - [JsonIgnore] - public override string Kind => "memory"; + /// Whether the session's policy service was available for the complete batch. When false, checks is empty and callers must treat every requested path as excluded. + [JsonPropertyName("available")] + public bool Available { get; set; } + + /// Per-path decisions in request order. Empty when available is false. + [JsonPropertyName("checks")] + public IList Checks { get => field ??= []; set; } } -/// Location-persisted tool approval details for a custom tool, keyed by tool name. -/// The custom-tool variant of . +/// Local file system absolute paths within the session working directory to check against its content-exclusion policy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsCustomTool : PermissionsLocationsAddToolApprovalDetails +internal sealed class ContentExclusionCheckPathsRequest { - /// - [JsonIgnore] - public override string Kind => "custom-tool"; + /// Local file system absolute paths within the session working directory to check. Results are returned in the same order, including duplicates. + [JsonPropertyName("paths")] + public IList Paths { get => field ??= []; set; } - /// Custom tool name. - [JsonPropertyName("toolName")] - public required string ToolName { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Location-persisted tool approval details for extension-management operations, optionally narrowed by operation. -/// The extension-management variant of . +/// Identifier of the spawned shell process, usable with shell.kill while the process is running. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsExtensionManagement : PermissionsLocationsAddToolApprovalDetails +public sealed class ShellExecResult { - /// - [JsonIgnore] - public override string Kind => "extension-management"; - - /// Optional operation identifier; when omitted, the approval covers all extension management operations. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("operation")] - public string? Operation { get; set; } + /// Identifier usable with shell.kill while the process is running. + [JsonPropertyName("processId")] + public string ProcessId { get; set; } = string.Empty; } -/// Location-persisted workflow approval, optionally narrowed by approval key. -/// The workflow variant of . +/// Shell command to run, with optional working directory and timeout in milliseconds. Spawn failures return an RPC error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsWorkflow : PermissionsLocationsAddToolApprovalDetails +internal sealed class ShellExecRequest { - /// - [JsonIgnore] - public override string Kind => "workflow"; + /// Shell command to execute. + [JsonPropertyName("command")] + public string Command { get; set; } = string.Empty; - /// Optional workflow operation name or canonical approval key; when omitted, the approval covers all workflow operations. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("approvalKey")] - public string? ApprovalKey { get; set; } + /// Working directory (defaults to session working directory). + [JsonPropertyName("cwd")] + public string? Cwd { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Timeout in milliseconds (default: 30000). + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonPropertyName("timeout")] + public TimeSpan? Timeout { get; set; } } -/// Location-persisted tool approval details for an extension's permission-gated capability access, keyed by extension name. -/// The extension-permission-access variant of . +/// Indicates whether the signal was delivered; false if the process was unknown or already exited. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess : PermissionsLocationsAddToolApprovalDetails +public sealed class ShellKillResult { - /// - [JsonIgnore] - public override string Kind => "extension-permission-access"; + /// Whether the signal was sent successfully. + [JsonPropertyName("killed")] + public bool Killed { get; set; } +} - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } +/// Identifier of a process previously returned by "shell.exec" and the signal to send. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class ShellKillRequest +{ + /// Process identifier returned by shell.exec. + [JsonPropertyName("processId")] + public string ProcessId { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// Signal to send (default: SIGTERM). + [JsonPropertyName("signal")] + public ShellKillSignal? Signal { get; set; } } -/// Location-persisted tool approval details for an extension's access to sensitive environment variables, keyed by extension name and the exact set of variable names. -/// The extension-env-access variant of . +/// Result of a user-requested shell command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class PermissionsLocationsAddToolApprovalDetailsExtensionEnvAccess : PermissionsLocationsAddToolApprovalDetails +public sealed class UserRequestedShellCommandResult { - /// - [JsonIgnore] - public override string Kind => "extension-env-access"; + /// Error output when the execution failed. + [JsonPropertyName("error")] + public string? Error { get; set; } - /// Names of the sensitive environment variables this approval covers. Values are never persisted. - [JsonPropertyName("environmentVariables")] - public required IList EnvironmentVariables { get; set; } + /// Process exit code, when available. + [JsonPropertyName("exitCode")] + public long? ExitCode { get; set; } - /// Extension name. - [JsonPropertyName("extensionName")] - public required string ExtensionName { get; set; } + /// Captured command output. + [JsonPropertyName("output")] + public string Output { get; set; } = string.Empty; + + /// Whether the command completed successfully. + [JsonPropertyName("success")] + public bool Success { get; set; } + + /// Tool call id emitted for the shell execution. + [JsonPropertyName("toolCallId")] + public string ToolCallId { get; set; } = string.Empty; } -/// Location-scoped tool approval to persist. +/// User-requested shell command and cancellation handle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionLocationAddToolApprovalParams +internal sealed class ShellExecuteUserRequestedRequest { - /// Tool approval to persist and apply. - [JsonPropertyName("approval")] - public PermissionsLocationsAddToolApprovalDetails Approval { get => field ??= new(); set; } + /// Shell command to execute. + [JsonPropertyName("command")] + public string Command { get; set; } = string.Empty; - /// Location key (git root or cwd) to persist the approval to. - [JsonPropertyName("locationKey")] - public string LocationKey { get; set; } = string.Empty; + /// Caller-provided cancellation handle for this execution. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Folder trust check result. +/// Cancellation result for a user-requested shell command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class FolderTrustCheckResult +public sealed class CancelUserRequestedShellCommandResult { - /// Whether the folder is trusted. - [JsonPropertyName("trusted")] - public bool Trusted { get; set; } + /// Whether an in-flight execution was found and signalled to cancel. + [JsonPropertyName("cancelled")] + public bool Cancelled { get; set; } } -/// Folder path to check for trust. +/// User-requested shell execution cancellation handle. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class FolderTrustCheckParams +internal sealed class ShellCancelUserRequestedRequest { - /// Folder path to check. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Request ID previously passed to executeUserRequested. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Indicates whether the operation succeeded. +/// Post-compaction context window usage breakdown. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsFolderTrustAddTrustedResult +public sealed class HistoryCompactContextWindow { - /// Whether the operation succeeded. + /// Token count from non-system messages (user, assistant, tool). + [JsonPropertyName("conversationTokens")] + public long? ConversationTokens { get; set; } + + /// Current total tokens in the context window (system + conversation + tool definitions). + [JsonPropertyName("currentTokens")] + public long CurrentTokens { get; set; } + + /// Current number of messages in the conversation. + [JsonPropertyName("messagesLength")] + public long MessagesLength { get; set; } + + /// Token count from system message(s). + [JsonPropertyName("systemTokens")] + public long? SystemTokens { get; set; } + + /// Maximum token count for the model's context window. + [JsonPropertyName("tokenLimit")] + public long TokenLimit { get; set; } + + /// Token count from tool definitions. + [JsonPropertyName("toolDefinitionsTokens")] + public long? ToolDefinitionsTokens { get; set; } +} + +/// 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; } + + /// Number of messages removed during compaction. + [JsonPropertyName("messagesRemoved")] + public long MessagesRemoved { get; set; } + + /// Whether compaction completed successfully. [JsonPropertyName("success")] public bool Success { get; set; } + + /// Summary text produced by compaction. Omitted when compaction did not produce a summary (e.g. failure path). + [JsonPropertyName("summaryContent")] + public string? SummaryContent { get; set; } + + /// Number of tokens freed by compaction. + [JsonPropertyName("tokensRemoved")] + public long TokensRemoved { get; set; } } -/// Folder path to add to trusted folders. +/// RPC data type for SessionHistoryCompact operations. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class FolderTrustAddParams +public sealed class SessionHistoryCompactRequest { - /// Folder path to mark as trusted. - [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; } + + /// Context window token limit this compaction is targeting, recorded as the `tokenLimit` on the persisted `session.compaction_start` / `session.compaction_complete` events. Set it when the compaction targets a window other than the compacting model's own, e.g. switching to a model with a smaller context window: the compaction still runs on the current model, so the limit that motivated it would otherwise be lost. When absent, the events record the compacting model's own resolved limit. Attribution metadata only - it does not change how much the compaction removes. + [JsonPropertyName("tokenLimit")] + public long? TokenLimit { get; set; } + + /// What initiated this compaction request, recorded as the `trigger` on the persisted `session.compaction_start` / `session.compaction_complete` events. When absent, the compaction is persisted without trigger attribution (initiator unknown). + [JsonPropertyName("trigger")] + public SessionHistoryCompactRequestTrigger? Trigger { get; set; } +} + +/// RPC data type for SessionHistoryCompactRequestWithSession operations. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionHistoryCompactRequestWithSession +{ + /// Optional user-provided instructions to focus the compaction summary. + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Safe for generated string properties: JSON Schema minLength/maxLength map to string length validation, not reflection over trimmed Count members")] + [MaxLength(4000)] + [JsonPropertyName("customInstructions")] + public string? CustomInstructions { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; + + /// Context window token limit this compaction is targeting, recorded as the `tokenLimit` on the persisted `session.compaction_start` / `session.compaction_complete` events. Set it when the compaction targets a window other than the compacting model's own, e.g. switching to a model with a smaller context window: the compaction still runs on the current model, so the limit that motivated it would otherwise be lost. When absent, the events record the compacting model's own resolved limit. Attribution metadata only - it does not change how much the compaction removes. + [JsonPropertyName("tokenLimit")] + public long? TokenLimit { get; set; } + + /// What initiated this compaction request, recorded as the `trigger` on the persisted `session.compaction_start` / `session.compaction_complete` events. When absent, the compaction is persisted without trigger attribution (initiator unknown). + [JsonPropertyName("trigger")] + public SessionHistoryCompactRequestTrigger? Trigger { get; set; } } -/// Indicates whether the operation succeeded. +/// Number of events that were removed by the truncation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class PermissionsUrlsSetUnrestrictedModeResult +public sealed class HistoryTruncateResult { - /// Whether the operation succeeded. - [JsonPropertyName("success")] - public bool Success { get; set; } + /// 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; } + + /// Number of events that were removed. + [JsonPropertyName("eventsRemoved")] + public long EventsRemoved { get; set; } } -/// Whether the URL-permission policy should run in unrestricted mode. +/// Identifier of the event to truncate to; this event and all later events are removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class PermissionUrlsSetUnrestrictedModeParams +internal sealed class HistoryTruncateRequest { - /// 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; } + /// 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; } -/// The repository the remote session targets. +/// A root user turn that the session can rewind to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataSnapshotRemoteMetadataRepository +public sealed class HistoryRewindPoint { - /// The branch the remote session is operating on. - [JsonPropertyName("branch")] - public string Branch { get; set; } = string.Empty; + /// Whether at least one file in this turn or a later turn can be restored. + [JsonPropertyName("canRestoreFiles")] + public bool CanRestoreFiles { get; set; } - /// The GitHub repository name (without owner). - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// ID of the user.message event that begins the discarded suffix. + [JsonPropertyName("eventId")] + public string EventId { get; set; } = string.Empty; - /// The GitHub owner (user or organization) of the target repository. - [JsonPropertyName("owner")] - public string Owner { 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; } + + /// User-visible message text for the turn. + [JsonPropertyName("userMessage")] + public string UserMessage { get; set; } = string.Empty; } -/// Remote-session-specific metadata. Populated only when `isRemote` is true. Fields are immutable for the lifetime of the session. +/// Rewind points and file-change-tracking availability for the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataSnapshotRemoteMetadata +public sealed class HistoryListRewindPointsResult { - /// The pull request number the remote session is associated with, if any. - [JsonPropertyName("pullRequestNumber")] - public long? PullRequestNumber { get; set; } + /// Whether this session captured file changes from its first turn. + [JsonPropertyName("fileChangeTrackingEnabled")] + public bool FileChangeTrackingEnabled { get; set; } - /// The repository the remote session targets. - [JsonPropertyName("repository")] - public MetadataSnapshotRemoteMetadataRepository Repository { get => field ??= new(); set; } + /// Root user turns in chronological order. Empty when `unavailableReason` is set. + [JsonPropertyName("points")] + public IList Points { 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; } + /// 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; } +} - /// Whether the remote task originated from Copilot Coding Agent (cca) or a CLI `--remote` invocation. - [JsonPropertyName("taskType")] - public MetadataSnapshotRemoteMetadataTaskType? TaskType { get; set; } +/// Identifies the target session. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class SessionHistoryListRewindPointsRequest +{ + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Public-facing projection of workspace metadata for SDK / TUI consumers. -public sealed class SessionMetadataSnapshotWorkspace +/// A file that a conversation-and-files rewind would restore. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistoryRewindFilePreview { - /// Branch checked out at session start, if any. - [JsonPropertyName("branch")] - public string? Branch { get; set; } + /// Aggregate change made across the discarded turns. + [JsonPropertyName("changeType")] + public HistoryRewindChangeType ChangeType { get; set; } - /// ISO 8601 timestamp when the workspace was created. - [JsonPropertyName("created_at")] - public DateTimeOffset? CreatedAt { get; set; } + /// Lines added across the discarded turns. + [JsonPropertyName("linesAdded")] + public long LinesAdded { get; set; } - /// Current working directory at session start. - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// Lines removed across the discarded turns. + [JsonPropertyName("linesRemoved")] + public long LinesRemoved { get; set; } - /// Resolved git root for cwd, if any. - [JsonPropertyName("git_root")] - public string? GitRoot { get; set; } + /// Absolute path of the captured file. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; +} - /// Repository host type, if known. - [JsonPropertyName("host_type")] - public WorkspaceSummaryHostType? HostType { get; set; } +/// Files and aggregate changes for a prospective rewind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistoryPreviewRewindResult +{ + /// Whether file restore is available for this session. This is authoritative: switch on it and read `reason` only when it is false. + [JsonPropertyName("available")] + public bool Available { get; set; } - /// 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; + /// Number of unique files in the preview. + [JsonPropertyName("fileCount")] + public long FileCount { get; set; } - /// Display name for the session, if set. - [JsonPropertyName("name")] - public string? Name { get; set; } + /// Files ordered by path. + [JsonPropertyName("files")] + public IList Files { get => field ??= []; set; } - /// Repository identifier in 'owner/repo' or 'org/project/repo' format, if any. - [JsonPropertyName("repository")] - public string? Repository { 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; } +} - /// ISO 8601 timestamp when the workspace was last updated. - [JsonPropertyName("updated_at")] - public DateTimeOffset? UpdatedAt { get; set; } +/// Event boundary to preview for conversation-and-files rewind. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HistoryPreviewRewindRequest +{ + /// ID of the user.message event that begins the discarded suffix. + [JsonPropertyName("eventId")] + public string EventId { 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; } -/// Point-in-time snapshot of slow-changing session identifier and state fields. +/// A captured file that rewind intentionally left unchanged. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionMetadataSnapshot +public sealed class HistorySkippedFileRestore { - /// 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; } + /// Absolute path of the skipped file. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Runtime client name associated with the session (telemetry identifier). - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } + /// Reason the file was not restored. + [JsonPropertyName("reason")] + public HistoryFileRestoreSkipReason Reason { get; set; } +} - /// The current agent mode for this session (e.g., 'interactive', 'plan', 'autopilot'). - [JsonPropertyName("currentMode")] - public MetadataSnapshotCurrentMode CurrentMode { get; set; } +/// Structured outcome of a rewind request. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistoryRewindResult +{ + /// 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; } - /// 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; } + /// 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; } - /// User-provided name supplied at session construction (via `--name`), if any. Immutable after construction. - [JsonPropertyName("initialName")] - public string? InitialName { 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; } - /// 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; } + /// 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; } - /// 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; } + /// 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; } +} - /// 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; } +/// Boundary and mode for rewinding session history. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HistoryRewindRequest +{ + /// ID of the user.message event that begins the discarded suffix. + [JsonPropertyName("eventId")] + public string EventId { get; set; } = string.Empty; - /// Currently selected model identifier, if any. - [JsonPropertyName("selectedModel")] - public string? SelectedModel { get; set; } + /// Whether to rewind only conversation history or also restore captured files. + [JsonPropertyName("mode")] + public HistoryRewindMode Mode { get; set; } - /// The unique identifier of the session. + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; +} - /// Current session limits, or null when no limits are active. - [JsonPropertyName("sessionLimits")] - public SessionLimitsConfig? SessionLimits { get; set; } - - /// ISO 8601 timestamp of when the session started. - [JsonPropertyName("startTime")] - public DateTimeOffset StartTime { 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; } - - /// Absolute path to the session's current working directory. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; +/// Indicates whether an in-progress background compaction was cancelled. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistoryCancelBackgroundCompactionResult +{ + /// 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; } +} - /// 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; } +/// 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; +} - /// 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; } +/// 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 SessionMetadataSnapshotRequest +internal sealed class SessionHistoryAbortManualCompactionRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } +/// Markdown summary of the conversation context (empty when not available). +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class HistorySummarizeForHandoffResult +{ + /// Markdown summary of the conversation context produced by an LLM. Empty string when there are no messages or when the session does not support local summarization. + [JsonPropertyName("summary")] + public string Summary { get; set; } = string.Empty; +} + /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataGetClientMetadataRequest +internal sealed class SessionHistorySummarizeForHandoffRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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. +/// What a successful clear removed. A clear that could not be applied rejects instead of reporting a count. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataUpdateClientMetadataRequest +public sealed class HistoryClearContextResult { - /// Remove every existing client metadata entry before applying remove and set. Defaults to false. - [JsonPropertyName("clear")] - public bool? Clear { 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; } +} - /// 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; } +/// Parameters for clearing the conversation and seeding the window that replaces it. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class HistoryClearContextRequest +{ + /// 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; +} - /// 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; } +/// User-facing pending queue entry, with kind and display text for a queued message, slash command, or model change. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueuePendingItems +{ + /// 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; } + + /// Human-readable text to display for this queue entry in the UI. + [JsonPropertyName("displayText")] + public string DisplayText { get; set; } = string.Empty; + + /// Stable opaque id for the canonical queued item. Batch rows share one id. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// 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; } } -/// Indicates whether the local session is currently processing a turn or background continuation. +/// Snapshot of the session's pending queued items and immediate-steering messages. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataIsProcessingResult +public sealed class QueuePendingItemsResult { - /// 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; } + /// How many leading entries of `steeringMessages` have already been folded into the running turn (and so have an emitted `user.message`), as opposed to still waiting for one. Absent for hosts that do not distinguish the two. + [JsonPropertyName("inFlightSteeringCount")] + public long? InFlightSteeringCount { get; set; } + + /// Pending queued items in submission order. Includes user messages, queued slash commands, and queued model changes; omits internal system items. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } + + /// Display text for messages currently in the immediate steering queue (interjections sent during a running turn). + [JsonPropertyName("steeringMessages")] + public IList SteeringMessages { get => field ??= []; set; } + + /// ID of the running turn's user message while the model has not answered it, so `withdrawMessage` can still take it back once nothing sent after it is pending. A message leaves `items` when its turn starts, before its `user.message` is recorded; this tells that message apart from one that was removed. Absent when no turn prompt can be taken back. + [JsonPropertyName("withdrawableTurnMessageId")] + public string? WithdrawableTurnMessageId { get; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataIsProcessingRequest +internal sealed class SessionQueuePendingItemsRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Current activity flags for the session. +/// Internal snapshot of native queue state for local session orchestration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionActivity +internal sealed class QueueSnapshotResult { - /// Whether an in-flight operation can currently be aborted. - [JsonPropertyName("abortable")] - public bool Abortable { get; set; } + /// Queue item identifier of a model switch that has been dequeued but not yet applied. + [JsonPropertyName("inFlightModelChangeId")] + public string? InFlightModelChangeId { get; set; } - /// Whether the session currently has active work, including running turns or tasks. - [JsonPropertyName("hasActiveWork")] - public bool HasActiveWork { get; set; } + /// Insertion orders for queued items, aligned with `items`. + [JsonPropertyName("itemOrders")] + public IList? ItemOrders { get; set; } + + /// User-facing pending items in FIFO order. + [JsonPropertyName("items")] + public IList Items { get => field ??= []; set; } + + /// Insertion orders for immediate steering messages, aligned with `steeringMessages`. + [JsonPropertyName("steeringMessageOrders")] + public IList? SteeringMessageOrders { get; set; } + + /// Immediate steering messages waiting for an active turn. + [JsonPropertyName("steeringMessages")] + public IList SteeringMessages { get => field ??= []; set; } } /// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionMetadataActivityRequest +internal sealed class SessionQueueSnapshotRequest { /// 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 +/// Result of moving a queued item. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueueMoveItemResult { - /// Output reserve plus tokens after the buffer-exhaustion blocking threshold (default 95%). - [JsonPropertyName("bufferTokens")] - public long BufferTokens { get; set; } + /// True when the item changed position; false when it was already at the requested position. + [JsonPropertyName("changed")] + public bool Changed { get; set; } +} - /// Token count at which background compaction starts (configurable percentage of promptTokenLimit). - [JsonPropertyName("compactionThreshold")] - public long CompactionThreshold { 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; - /// Tokens consumed by user/assistant/tool messages. - [JsonPropertyName("conversationTokens")] - public long ConversationTokens { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// Prompt token limit plus the model's full output token limit. - [JsonPropertyName("limit")] - public long Limit { get; set; } + /// Zero-based target position in the public visible queue. Values outside the queue clamp to an end. + [JsonPropertyName("toPosition")] + public long ToPosition { get; set; } +} - /// Tokens consumed by MCP tool definitions (subset of toolDefinitionsTokens, excludes deferred tools). - [JsonPropertyName("mcpToolsTokens")] - public long McpToolsTokens { get; set; } +/// Result of inserting a queued message. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueueInsertAtResult +{ + /// Fresh stable opaque id assigned to the inserted item. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; +} - /// The model used for token counting. - [JsonPropertyName("modelName")] - public string ModelName { get; set; } = string.Empty; +/// 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; } - /// Maximum prompt tokens allowed by the model (or DEFAULT_TOKEN_LIMIT if unspecified). - [JsonPropertyName("promptTokenLimit")] - public long PromptTokenLimit { get; set; } + /// Optional attachments for the message. + [JsonPropertyName("attachments")] + public IList? Attachments { get; set; } - /// Tokens consumed by the system prompt. - [JsonPropertyName("systemTokens")] - public long SystemTokens { get; set; } + /// Whether the message is billable. + [JsonPropertyName("billable")] + public bool? Billable { get; set; } - /// Tokens consumed by tool definitions sent to the model (excludes deferred tools). - [JsonPropertyName("toolDefinitionsTokens")] - public long ToolDefinitionsTokens { get; set; } + /// Accepted for internal SendOptions compatibility but ignored; delivery is derived from current session activity. + [JsonPropertyName("delivery")] + public string? Delivery { get; set; } - /// Sum of system, conversation and tool-definition tokens. - [JsonPropertyName("totalTokens")] - public long TotalTokens { get; set; } -} + /// Optional user-facing display text. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } -/// Token breakdown for the session's current context window, or null if uninitialized. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataContextInfoResult -{ - /// 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; } + /// Accepted for SendOptions compatibility but ignored; inserted items always use queued delivery semantics. + [JsonPropertyName("mode")] + public SendMode? Mode { get; set; } + + /// Accepted for SendOptions compatibility but ignored; the requested public position controls placement. + [JsonPropertyName("prepend")] + public bool? Prepend { get; set; } + + /// The user message text. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + + /// Per-turn request headers. + [JsonPropertyName("requestHeaders")] + public IDictionary? RequestHeaders { get; set; } + + /// Required tool name for the turn, when any. + [JsonPropertyName("requiredTool")] + public string? RequiredTool { get; set; } + + /// Optional provenance source. `system` is rejected: it would hide the inserted row from `pendingItems` and make it unaddressable while still executing, so inserted items must stay visible. + [JsonPropertyName("source")] + public string? Source { get; set; } + + /// Accepted for SendOptions compatibility but ignored; insertion scheduling is controlled by the queue drain state. + [JsonPropertyName("wait")] + public bool? Wait { get; set; } } -/// Model identifier and token limits used to compute the context-info breakdown. +/// Parameters for inserting a queued message at a public visible position. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataContextInfoRequest +internal sealed class QueueInsertAtRequest { - /// Maximum output tokens allowed by the target model. Pass 0 if unknown. - [JsonPropertyName("outputTokenLimit")] - public long OutputTokenLimit { get; set; } - - /// Maximum prompt tokens allowed by the target model. Pass 0 to use the runtime default. - [JsonPropertyName("promptTokenLimit")] - public long PromptTokenLimit { get; set; } + /// Queued message contents and delivery metadata. + [JsonPropertyName("message")] + public QueueInsertMessage Message { get => field ??= new(); 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; } + /// 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; } -/// 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 +/// Result of removing a queued item. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueueRemoveAtResult { - /// Output reserve 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; } + /// True when the addressed item was removed. + [JsonPropertyName("removed")] + public bool Removed { get; set; } +} - /// System prompt tokens, excluding custom instructions. - [JsonPropertyName("systemPrompt")] - public long SystemPrompt { get; set; } +/// Parameters for removing a queued item by stable id. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueRemoveAtRequest +{ + /// Stable opaque ID of the queued item to remove. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; - /// 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 editing a queued message. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueueUpdateTextResult { - /// Number of successful compactions in this session. - [JsonPropertyName("count")] - public long Count { get; set; } + /// True when the stored text changed. + [JsonPropertyName("updated")] + public bool Updated { get; set; } } -/// RPC data type for MetadataContextAttributionResultContextAttributionEntry operations. -public sealed class MetadataContextAttributionResultContextAttributionEntry +/// Parameters for editing a single queued message. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueUpdateTextRequest { - /// 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; } + /// Optional replacement prompt displayed to the user. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { 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 edit. [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; + /// Replacement prompt sent to the model. + [JsonPropertyName("prompt")] + public string Prompt { 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; } +/// Result of withdrawing a user message. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QueueWithdrawMessageResult +{ + /// 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; } - /// Token count currently in context attributable to this entry. - [JsonPropertyName("tokens")] - public long Tokens { get; set; } + /// True when the message left the queue or, for a running turn, history. + [JsonPropertyName("removed")] + public bool Removed { get; set; } } -/// Per-source token attribution snapshot for the current context window. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. -public sealed class MetadataContextAttributionResultContextAttribution +/// 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 QueueWithdrawMessageRequest { - /// Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. - [JsonPropertyName("bufferTokens")] - public long BufferTokens { get; set; } + /// 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; - /// 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; } + /// Message identity returned by send, not the queue item id. Batch messages are not eligible. + [JsonPropertyName("messageId")] + public string MessageId { get; set; } = string.Empty; - /// 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; } +/// Append to one pending steering message without changing its identity or delivery position. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueAppendSteeringRequest +{ + /// Mode captured at submission. Only steering messages in the same mode may be combined. + [JsonPropertyName("agentMode")] + public SendAgentMode AgentMode { 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; } + /// Attachments to add after the message's existing attachments. An empty list preserves the existing attachments. + [JsonPropertyName("attachments")] + public IList Attachments { get => field ??= []; set; } - /// Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. - [JsonPropertyName("limit")] - public long Limit { get; set; } + /// Display text to append to the existing preview after a blank line. + [JsonPropertyName("displayPrompt")] + public string DisplayPrompt { get; set; } = string.Empty; - /// The concrete model id the entire breakdown was tokenized against (feeds the per-model token multiplier). Under `Auto` (Free/Student) this is the resolved model, not the literal `auto` sentinel, so totals are not undercounted. A single-model approximation of a potentially multi-model Auto session. - [JsonPropertyName("modelId")] - public string ModelId { get; set; } = string.Empty; + /// 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; - /// 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; + /// 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; - /// Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. - [JsonPropertyName("promptTokenLimit")] - public long PromptTokenLimit { get; set; } + /// Text to append after a blank line. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; - /// 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. +/// Result of duplicating a queued item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataContextAttributionResult +public sealed class QueueDuplicateAtResult { - /// 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; } + /// Fresh stable opaque id assigned to the duplicate. + [JsonPropertyName("id")] + public string Id { 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; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// 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 SessionMetadataGetContextAttributionRequest +internal sealed class QueueSetDrainPausedRequest { + /// Whether queued-lane draining should be paused. + [JsonPropertyName("paused")] + public bool Paused { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// A single large message currently in context. +/// Result of trying to steer a queued message into a live turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ContextHeaviestMessage +public sealed class QueueSendNowResult { - /// Stable identifier for this message within the snapshot. + /// True when the item was accepted into the steering lane; false when no main turn was live. + [JsonPropertyName("steered")] + public bool Steered { get; set; } +} + +/// Parameters for steering a queued message into a live turn. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class QueueSendNowRequest +{ + /// Stable opaque ID of the queued item to steer into the live turn. [JsonPropertyName("id")] public string Id { get; set; } = string.Empty; - /// Human-readable source label, e.g. `tool: bash` or `skill: tmux`. Presentation-only. - [JsonPropertyName("label")] - public string Label { get; set; } = string.Empty; - - /// 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; } + /// 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. +/// Whether the native queue has pending work. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataContextHeaviestMessagesResult +internal sealed class QueueHasPendingResult { - /// 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 queued or immediate native work is pending. + [JsonPropertyName("hasPending")] + public bool HasPending { 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 SessionQueueHasPendingRequest { - /// 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. +/// Whether a deferred-idle drain should run. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataRecordContextChangeResult +internal sealed class QueueBeginDeferredIdleDrainResult { + /// True when the host should run finishDeferredIdleDrain asynchronously. + [JsonPropertyName("shouldDrain")] + public bool ShouldDrain { get; set; } } -/// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. +/// Inputs for starting a deferred-idle drain. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionWorkingDirectoryContext +internal sealed class QueueBeginDeferredIdleDrainRequest { - /// 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; } + /// Whether the host still has active background work. + [JsonPropertyName("activeBackgroundWork")] + public bool ActiveBackgroundWork { get; set; } - /// Current working directory path. - [JsonPropertyName("cwd")] - public string Cwd { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Root directory of the git repository, resolved via git rev-parse. - [JsonPropertyName("gitRoot")] - public string? GitRoot { 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; } - /// Head commit of the current git branch. - [JsonPropertyName("headCommit")] - public string? HeadCommit { get; set; } + /// One of none, processQueue, or emitSessionIdle. + [JsonPropertyName("action")] + public string Action { get; set; } = string.Empty; +} - /// Hosting platform type of the repository. - [JsonPropertyName("hostType")] - public SessionWorkingDirectoryContextHostType? HostType { 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; } - /// 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 native queued work remains. + [JsonPropertyName("hasPending")] + public bool HasPending { get; set; } - /// Raw host string from the git remote URL (e.g. "github.com", "dev.azure.com"). - [JsonPropertyName("repositoryHost")] - public string? RepositoryHost { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Updated working-directory/git context to record on the session. +/// Inputs for marking session.idle deferred in native state. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataRecordContextChangeRequest +internal sealed class QueueDeferSessionIdleRequest { - /// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. - [JsonPropertyName("context")] - public SessionWorkingDirectoryContext Context { get => field ??= new(); set; } + /// Whether the deferred idle was caused by an aborted foreground turn. + [JsonPropertyName("aborted")] + public bool Aborted { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// 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. +/// Indicates whether a user-facing pending item was removed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataSetWorkingDirectoryResult +public sealed class QueueRemoveMostRecentResult { - /// Working directory after the update. - [JsonPropertyName("workingDirectory")] - public string WorkingDirectory { get; set; } = string.Empty; + /// 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; } } -/// 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 SessionQueueRemoveMostRecentRequest { /// 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. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class MetadataRecomputeContextTokensResult -{ - /// 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; } } -/// Model identifier to use when re-tokenizing the session's existing messages. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class MetadataRecomputeContextTokensRequest +internal sealed class SessionQueueClearRequest { - /// Model identifier used for tokenization. The runtime token-counts both chat-context and system-context messages against this model. - [JsonPropertyName("modelId")] - public string ModelId { get; set; } = string.Empty; - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Availability of built-in job tools surfaced to boundary consumers. +/// Internal filter for consuming queued system notifications. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsBuiltInToolAvailabilitySnapshot +internal sealed class QueueConsumeSystemNotificationsRequest { - /// Whether the create-pull-request tool is available. - [JsonPropertyName("createPullRequest")] - public bool? CreatePullRequest { get; set; } + /// Opaque runtime-owned filter object. + [JsonPropertyName("filter")] + public JsonElement Filter { get; set; } - /// Whether the report-progress tool is available. - [JsonPropertyName("reportProgress")] - public bool? ReportProgress { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Redacted job settings for a session. The job nonce is excluded. +/// Result of enqueueing the resume-pending wake item. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsJobSnapshot +internal sealed class QueueEnqueueResumePendingResult { - /// Availability of job-specific built-in tools. - [JsonPropertyName("builtInToolAvailability")] - public SessionSettingsBuiltInToolAvailabilitySnapshot? BuiltInToolAvailability { get; set; } - - /// GitHub Actions event type for the job. - [JsonPropertyName("eventType")] - public string? EventType { get; set; } - - /// Whether this is the workflow's trigger job. - [JsonPropertyName("isTriggerJob")] - public bool? IsTriggerJob { get; set; } + /// True when a wake item was newly queued. + [JsonPropertyName("queued")] + public bool Queued { get; set; } } -/// Redacted model routing settings for a session. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsModelSnapshot +internal sealed class SessionQueueEnqueueResumePendingRequest { - /// Agent service callback URL for job and progress updates. - [JsonPropertyName("callbackUrl")] - public string? CallbackUrl { get; set; } - - /// Default reasoning effort for the selected model. - [JsonPropertyName("defaultReasoningEffort")] - public string? DefaultReasoningEffort { get; set; } - - /// Agent job identifier for the session. - [JsonPropertyName("instanceId")] - public string? InstanceId { get; set; } - - /// Selected model identifier. - [JsonPropertyName("model")] - public string? Model { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Online-evaluation settings safe to expose across the SDK boundary. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsOnlineEvaluationSnapshot +internal sealed class SessionQueueProcessRequest { - /// Whether online evaluation is disabled. - [JsonPropertyName("disableOnlineEvaluation")] - public bool? DisableOnlineEvaluation { get; set; } - - /// Whether online-evaluation output-file generation is enabled. - [JsonPropertyName("enableOnlineEvaluationOutputFile")] - public bool? EnableOnlineEvaluationOutputFile { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Redacted repository and GitHub host settings for a session. +/// Cursor, batch size, and optional long-poll/filter parameters for reading session events. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionSettingsRepoSnapshot +internal sealed class EventLogReadRequest { - /// Checked-out repository branch. - [JsonPropertyName("branch")] - public string? Branch { get; set; } - - /// Checked-out commit SHA. - [JsonPropertyName("commit")] - public string? Commit { get; set; } - - /// GitHub server host name. - [JsonPropertyName("host")] - public string? Host { get; set; } - - /// Protocol used to access the GitHub host. - [JsonPropertyName("hostProtocol")] - public string? HostProtocol { get; set; } + /// 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; } - /// GitHub repository database ID. - [JsonPropertyName("id")] - public double? Id { 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; } - /// Repository name. - [JsonPropertyName("name")] - public string? Name { 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; } - /// GitHub repository owner database ID. - [JsonPropertyName("ownerId")] - public double? OwnerId { 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; } - /// Repository owner login. - [JsonPropertyName("ownerName")] - public string? OwnerName { 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; } - /// Number of commits in the pull request. - [JsonPropertyName("prCommitCount")] - public double? PrCommitCount { get; set; } + /// Maximum number of events to return in this batch (1–1000, default 200). + [JsonPropertyName("max")] + public long? Max { get; set; } - /// Whether the repository is writable. - [JsonPropertyName("readWrite")] - public bool? ReadWrite { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; - /// GitHub secret-scanning service URL. - [JsonPropertyName("secretScanningUrl")] - public string? SecretScanningUrl { get; set; } + /// Either '*' to receive all event types, or a non-empty list of event types to receive. + [JsonPropertyName("types")] + public JsonElement? Types { get; set; } - /// GitHub server base URL. - [JsonPropertyName("serverUrl")] - public string? ServerUrl { 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; } } -/// Redacted validation and memory-tool settings for a session. +/// 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 SessionSettingsValidationSnapshot +public sealed class EventLogTailResult { - /// Whether advisory validation is enabled. - [JsonPropertyName("advisoryEnabled")] - public bool? AdvisoryEnabled { get; set; } - - /// Whether CodeQL validation is enabled. - [JsonPropertyName("codeqlEnabled")] - public bool? CodeqlEnabled { get; set; } - - /// Whether code-review validation is enabled. - [JsonPropertyName("codeReviewEnabled")] - public bool? CodeReviewEnabled { get; set; } - - /// Model used for code-review validation. - [JsonPropertyName("codeReviewModel")] - public string? CodeReviewModel { get; set; } - - /// Dependabot validation timeout budget in seconds. - [JsonPropertyName("dependabotTimeout")] - public double? DependabotTimeout { get; set; } - - /// Whether the memory-store tool is enabled. - [JsonPropertyName("memoryStoreEnabled")] - public bool? MemoryStoreEnabled { get; set; } - - /// Whether the memory-vote tool is enabled. - [JsonPropertyName("memoryVoteEnabled")] - public bool? MemoryVoteEnabled { get; set; } - - /// Whether secret-scanning validation is enabled. - [JsonPropertyName("secretScanningEnabled")] - public bool? SecretScanningEnabled { get; set; } - - /// General validation timeout budget in seconds. - [JsonPropertyName("timeout")] - public double? Timeout { get; set; } + /// 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; } -/// Redacted, serializable view of session runtime settings for SDK boundary consumers. Secrets and raw feature flags are intentionally excluded. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsSnapshot +internal sealed class SessionEventLogTailRequest { - /// Name of the SDK client that created the session. - [JsonPropertyName("clientName")] - public string? ClientName { get; set; } - - /// Redacted job settings. - [JsonPropertyName("job")] - public SessionSettingsJobSnapshot Job { get => field ??= new(); set; } - - /// Redacted model routing settings. - [JsonPropertyName("model")] - public SessionSettingsModelSnapshot Model { get => field ??= new(); 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; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Session start time as Unix epoch milliseconds. - [JsonPropertyName("startTimeMs")] - public double? StartTimeMs { get; set; } +/// Opaque handle representing an event-type interest registration. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class RegisterEventInterestResult +{ + /// 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; +} - /// Session timeout in milliseconds. - [JsonPropertyName("timeoutMs")] - public double? TimeoutMs { get; set; } +/// Event type to register consumer interest for, used by runtime gating logic. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +internal sealed class RegisterEventInterestParams +{ + /// The event type the consumer wants the runtime to treat as 'observed' for behavior-switching gating. Some runtime code paths inspect whether any consumer is interested in a specific event type and choose a different implementation accordingly (e.g. `mcp.oauth_required`: when interest is registered the runtime delegates interactive OAuth token acquisition to the consumer via `mcp.oauth_required` events; when no interest is registered the runtime still attempts non-interactive reconnect from cached or refreshable tokens, and only marks the server `needs-auth` if usable credentials are unavailable — it does not open a browser or start interactive OAuth without a consumer). SDK clients that long-poll events do NOT automatically appear as listeners to these gating checks — they must explicitly call `registerInterest` for each event type they want the runtime to count as having a consumer. Multiple registrations for the same event type from the same or different consumers are tracked independently and must each be released. See: `mcp.oauth_required`, `sampling.requested`, `auto_mode_switch.requested`, `session_limits_exhausted.requested`, `user_input.requested`, `elicitation.requested`, `command.queued`, `exit_plan_mode.requested`. + [JsonPropertyName("eventType")] + public string EventType { get; set; } = string.Empty; - /// Redacted validation and memory-tool settings. - [JsonPropertyName("validation")] - public SessionSettingsValidationSnapshot Validation { get => field ??= new(); set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Agent runtime version selector copied from the session settings, such as `latest` or a runtime release identifier. - [JsonPropertyName("version")] - public string? Version { get; set; } +/// 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; } } -/// Identifies the target session. +/// Opaque handle previously returned by `registerInterest` to release. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsSnapshotRequest +internal sealed class ReleaseEventInterestParams { + /// Handle returned by a previous `registerInterest` call. Idempotent: releasing an unknown or already-released handle is a no-op (returns success). When the last outstanding handle for an event type is released, the runtime reverts to its 'no consumer' code path for that event type. + [JsonPropertyName("handle")] + public string Handle { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of evaluating a Rust-owned settings predicate. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsEvaluatePredicateResult +internal sealed class SessionUsageGetMetricsRequest { - /// Whether the named settings predicate evaluated to enabled. - [JsonPropertyName("enabled")] - public bool Enabled { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Named Rust-owned settings predicate to evaluate for this session. +/// Internal absolute code-change totals reported by the owning host. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionSettingsEvaluatePredicateRequest +internal sealed class SessionUsageSetCodeChangesRequest { - /// Predicate name. The runtime owns the raw feature-flag names and composition logic. - [JsonPropertyName("name")] - public SessionSettingsPredicateName Name { get; set; } + /// Absolute changed-file count; omission preserves the previous count. + [JsonPropertyName("filesCount")] + public double? FilesCount { get; set; } + + /// Absolute added-line total, replacing the previous reading. + [JsonPropertyName("linesAdded")] + public double LinesAdded { get; set; } + + /// Absolute removed-line total, replacing the previous reading. + [JsonPropertyName("linesRemoved")] + public double LinesRemoved { get; set; } /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; +} - /// Tool name for tool-scoped predicates such as trivial-change handling. - [JsonPropertyName("toolName")] - public string? ToolName { get; set; } +/// Existing delegation guidance for an account without delegation access. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionQuotaDelegateWarning +{ + /// 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; } -/// Content-exclusion decision for one requested path. +/// Session quota snapshot, preserving the incumbent numeric units and reset metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ContentExclusionPathCheck +public sealed class SessionQuotaSnapshot { - /// Whether the session's complete content-exclusion policy excludes the path. - [JsonPropertyName("excluded")] - public bool Excluded { get; set; } + /// Entitled quantity in this quota's units. + [JsonPropertyName("entitlementRequests")] + public double EntitlementRequests { get; set; } - /// The path supplied by the caller. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// 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; } } -/// Batch content-exclusion result. Callers must fail closed when policy evaluation is unavailable. +/// The incumbent session-owned quota and account projection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ContentExclusionCheckPathsResult +public sealed class SessionQuotaGetResult { - /// 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; } + /// 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; } + + /// Whether model cost columns should be shown. + [JsonPropertyName("modelCostColumnVisible")] + public bool ModelCostColumnVisible { get; set; } + + /// Resolved Copilot plan tier. + [JsonPropertyName("planTier")] + public SessionQuotaPlanTier PlanTier { get; set; } + + /// Whether premium-request costs are billable. + [JsonPropertyName("premiumRequestsBillable")] + public bool PremiumRequestsBillable { get; set; } - /// Per-path decisions in request order. Empty when available is false. - [JsonPropertyName("checks")] - public IList Checks { get => field ??= []; set; } + /// Provider-owned account quota state, including snapshots without numeric balances. + [JsonPropertyName("providerQuotas")] + public IList? ProviderQuotas { 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; } } -/// Local file system absolute paths within the session working directory to check against its content-exclusion policy. +/// Identifies the target session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ContentExclusionCheckPathsRequest +internal sealed class SessionQuotaGetRequest { - /// 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; } - /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifier of the spawned process, used to correlate streamed output and exit notifications. +/// The incumbent session-owned quota and account projection. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ShellExecResult +public sealed class SessionQuotaRefreshResult { - /// Unique identifier for tracking streamed output. - [JsonPropertyName("processId")] - public string ProcessId { get; set; } = string.Empty; -} + /// Whether the account can sign up for Copilot Free. + [JsonPropertyName("canSignupForCopilotFree")] + public bool CanSignupForCopilotFree { get; set; } -/// Shell command to run, with optional working directory and timeout in milliseconds. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ShellExecRequest -{ - /// Shell command to execute. - [JsonPropertyName("command")] - public string Command { get; set; } = string.Empty; + /// Whether the account can delegate tasks to GitHub. + [JsonPropertyName("delegateAvailable")] + public bool DelegateAvailable { get; set; } - /// Working directory (defaults to session working directory). - [JsonPropertyName("cwd")] - public string? Cwd { get; set; } + /// Existing delegation warning for a free account. + [JsonPropertyName("delegateWarning")] + public SessionQuotaDelegateWarning? DelegateWarning { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Whether dynamic workflows are enabled for the active account. + [JsonPropertyName("dynamicWorkflowsEnabled")] + public bool DynamicWorkflowsEnabled { get; set; } - /// Timeout in milliseconds (default: 30000). - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonPropertyName("timeout")] - public TimeSpan? Timeout { get; set; } -} + /// Whether dynamic workflows are visible under the session's feature flags. + [JsonPropertyName("dynamicWorkflowsUiVisible")] + public bool DynamicWorkflowsUiVisible { 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; } + /// Whether the account uses the free limited Copilot plan. + [JsonPropertyName("isFreeUser")] + public bool IsFreeUser { get; set; } + + /// Whether the account uses token-based billing. + [JsonPropertyName("isTbbUser")] + public bool IsTbbUser { get; set; } + + /// Whether model cost columns should be shown. + [JsonPropertyName("modelCostColumnVisible")] + public bool ModelCostColumnVisible { get; set; } + + /// Resolved Copilot plan tier. + [JsonPropertyName("planTier")] + public SessionQuotaPlanTier PlanTier { get; set; } + + /// Whether premium-request costs are billable. + [JsonPropertyName("premiumRequestsBillable")] + public bool PremiumRequestsBillable { get; set; } + + /// Provider-owned account quota state, including snapshots without numeric balances. + [JsonPropertyName("providerQuotas")] + public IList? ProviderQuotas { get; set; } + + /// 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; } + + /// 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; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Register a self-paced scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueuePendingItemsRequest +internal sealed class ScheduleAddSelfPacedRequest { + /// Optional display-only prompt label. + [JsonPropertyName("displayPrompt")] + public string? DisplayPrompt { get; set; } + + /// Prompt text to enqueue when the schedule fires. + [JsonPropertyName("prompt")] + public string Prompt { get; set; } = string.Empty; + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Internal snapshot of native queue state for local session orchestration. +/// Re-arm a self-paced scheduled prompt. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueSnapshotResult +internal sealed class ScheduleRearmSelfPacedRequest { - /// 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; } + /// Epoch milliseconds when the prompt should next fire. + [JsonPropertyName("at")] + public long At { get; set; } - /// User-facing pending items in FIFO order. - [JsonPropertyName("items")] - public IList Items { get => field ??= []; set; } + /// Id of the self-paced scheduled prompt. + [JsonPropertyName("id")] + public long Id { get; set; } - /// Insertion orders for immediate steering messages, aligned with `steeringMessages`. - [JsonPropertyName("steeringMessageOrders")] - public IList? SteeringMessageOrders { get; set; } + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Immediate steering messages waiting for an active turn. - [JsonPropertyName("steeringMessages")] - public IList SteeringMessages { get => field ??= []; set; } +/// 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 ScheduleStopResult +{ + /// The removed entry, or omitted if no entry matched. + [JsonPropertyName("entry")] + public ScheduleEntry? Entry { get; set; } } -/// Identifies the target session. +/// Identifier of the scheduled prompt to remove. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueSnapshotRequest +internal sealed class ScheduleStopRequest { + /// Id of the scheduled prompt to remove. + [JsonPropertyName("id")] + public long Id { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Result of moving a queued item. +/// A bearer token supplied by the SDK client for a BYOK provider. The runtime sets it as `Authorization: Bearer <token>` on the outbound request and does no caching; the SDK consumer owns token caching and refresh. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueMoveItemResult +public sealed class ProviderTokenAcquireResult { - /// True when the item changed position; false when it was already at the requested position. - [JsonPropertyName("changed")] - public bool Changed { get; set; } + /// The bearer token value (without the `Bearer ` prefix). + [JsonPropertyName("token")] + public string Token { get; set; } = string.Empty; } -/// Parameters for moving a queued item by stable id. +/// Asks the SDK client to acquire a bearer token for a BYOK provider whose config set `hasBearerTokenProvider: true`. Issued by the runtime before each outbound model request; the runtime does no caching, so this is sent once per request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueMoveItemRequest +public sealed class ProviderTokenAcquireRequest { - /// Stable opaque queued-item id. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Name of the BYOK provider needing a token. For the legacy whole-session provider this is the implicit provider name; for named providers it is the configured provider name. + [JsonPropertyName("providerName")] + public string ProviderName { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; - - /// 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. +/// Result returned by an extension workflow closure. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueInsertAtResult +public sealed class WorkflowExecuteResult { - /// Fresh stable opaque id assigned to the inserted item. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Workflow result value. + [JsonPropertyName("result")] + public JsonElement? Result { get; set; } } -/// Serializable message fields accepted by queue.insertAt. +/// Parameters sent to the owning extension to execute a workflow closure. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueInsertMessage +public sealed class WorkflowExecuteRequest { - /// Optional explicit agent mode. When omitted, the session's current mode is assigned. - [JsonPropertyName("agentMode")] - public SendAgentMode? AgentMode { get; set; } + /// Workflow input value. + [JsonPropertyName("args")] + public JsonElement Args { get; set; } - /// Optional attachments for the message. - [JsonPropertyName("attachments")] - public IList? Attachments { get; set; } + /// Opaque token identifying this workflow execution attempt. + [JsonPropertyName("executionToken")] + public string ExecutionToken { get; set; } = string.Empty; - /// Whether the message is billable. - [JsonPropertyName("billable")] - public bool? Billable { get; set; } + /// Registered workflow name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Accepted for internal SendOptions compatibility but ignored; delivery is derived from current session activity. - [JsonPropertyName("delivery")] - public string? Delivery { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// Optional user-facing display text. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Target session identifier. + [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; } +/// 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; - /// Accepted for SendOptions compatibility but ignored; the requested public position controls placement. - [JsonPropertyName("prepend")] - public bool? Prepend { get; set; } + /// Workflow run identifier. + [JsonPropertyName("runId")] + public string RunId { get; set; } = string.Empty; - /// The user message text. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} - /// Per-turn request headers. - [JsonPropertyName("requestHeaders")] - public IDictionary? RequestHeaders { get; set; } +/// 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; } +} - /// Required tool name for the turn, when any. - [JsonPropertyName("requiredTool")] - public string? RequiredTool { get; set; } +/// 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; - /// 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; } + /// Owner-scoped task key included for correlation. + [JsonPropertyName("clientTaskId")] + public string ClientTaskId { 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; } + /// Canonical runtime-generated task identifier. + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + /// Reason the runtime requests cancellation. + [JsonPropertyName("reason")] + public ClientTaskCancelReason Reason { get; set; } + + /// Session that owns the client task. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Parameters for inserting a queued message at a public visible position. +/// Describes a filesystem error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueInsertAtRequest +public sealed class SessionFsError { - /// Queued message contents and delivery metadata. + /// Error classification. + [JsonPropertyName("code")] + public SessionFsErrorCode Code { get; set; } + + /// Free-form detail about the error, for logging/diagnostics. [JsonPropertyName("message")] - public QueueInsertMessage Message { get => field ??= new(); set; } + public string? Message { get; set; } - /// Zero-based position in the public visible queue. Values outside the queue clamp to an end. - [JsonPropertyName("position")] - public long Position { get; set; } + /// 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; } +} + +/// 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; + + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } +} + +/// 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 removing a queued item. +/// File bytes as standard base64, or a filesystem error if the read failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueRemoveAtResult +public sealed class SessionFsReadFileBytesResult { - /// True when the addressed item was removed. - [JsonPropertyName("removed")] - public bool Removed { get; set; } + /// Exact file bytes encoded as standard base64. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } } -/// Parameters for removing a queued item by stable id. +/// Path of the binary file to read from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueRemoveAtRequest +public sealed class SessionFsReadFileBytesRequest { - /// Stable opaque ID of the queued item to remove. - [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; } -/// Result of editing a queued message. +/// File path, content to write, and optional mode for the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueUpdateTextResult +public sealed class SessionFsWriteFileRequest { - /// True when the stored text changed. - [JsonPropertyName("updated")] - public bool Updated { get; set; } + /// Content to write. + [JsonPropertyName("content")] + public string Content { get; set; } = string.Empty; + + /// Optional POSIX-style mode for newly created files. + [JsonPropertyName("mode")] + public long? Mode { get; set; } + + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// Parameters for editing a single queued message. +/// File path, standard-base64-encoded bytes to write, and optional mode for the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueUpdateTextRequest +public sealed class SessionFsWriteFileBytesRequest { - /// Optional replacement prompt displayed to the user. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Exact file bytes encoded as standard base64. + [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; + /// Optional POSIX-style mode for newly created files. + [JsonPropertyName("mode")] + public long? Mode { get; set; } - /// Replacement prompt sent to the model. - [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 withdrawing a user message. +/// 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 QueueWithdrawMessageResult +public sealed class SessionFsAppendFileRequest { - /// 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; } + /// Content to append. + [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; } + /// 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; } -/// Conditional withdrawal of a single user message, from its queue or from the running turn it started. +/// Indicates whether the requested path exists in the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueWithdrawMessageRequest +public sealed class SessionFsExistsResult { - /// 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; + /// Whether the path exists. + [JsonPropertyName("exists")] + public bool Exists { get; set; } +} - /// Message identity returned by send, not the queue item id. Batch messages are not eligible. - [JsonPropertyName("messageId")] - public string MessageId { get; set; } = string.Empty; +/// 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; + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; +} + +/// Filesystem metadata for the requested path, or a filesystem error if the stat failed. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class SessionFsStatResult +{ + /// ISO 8601 timestamp of creation. + [JsonPropertyName("birthtime")] + public DateTimeOffset Birthtime { get; set; } + + /// 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; } +} + +/// 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; /// 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. +/// 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 QueueAppendSteeringRequest +public sealed class SessionFsMkdirRequest { - /// 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; + /// Optional POSIX-style mode for newly created directories. + [JsonPropertyName("mode")] + public long? Mode { 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; + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; - /// Text to append after a blank line. - [JsonPropertyName("prompt")] - public string Prompt { 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; } -/// Result of duplicating a queued item. +/// Names of entries in the requested directory, or a filesystem error if the read failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueDuplicateAtResult +public sealed class SessionFsReaddirResult { - /// Fresh stable opaque id assigned to the duplicate. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Entry names in the directory. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } + + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } } -/// Parameters for duplicating a queued item. +/// Directory path whose entries should be listed from the client-provided session filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueDuplicateAtRequest +public sealed class SessionFsReaddirRequest { - /// Stable opaque ID of the queued item to duplicate. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Parameters for acquiring or releasing the queued-lane drain pause. Acquisition is exclusive and non-idempotent: `paused: true` against an already-paused session fails with `queue_already_paused`. The pause is never released automatically — it is not tied to the caller's lifetime, so a client that exits without sending `paused: false` leaves the lane frozen. Release is unowned: `paused: false` clears the pause for any caller, including one that never acquired it. +/// Directory entry returned by session filesystem `readdirWithTypes`, with name and entry type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueSetDrainPausedRequest +public sealed class SessionFsReaddirWithTypesEntry { - /// Whether queued-lane draining should be paused. - [JsonPropertyName("paused")] - public bool Paused { get; set; } + /// Entry name. + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Entry type. + [JsonPropertyName("type")] + public SessionFsReaddirWithTypesEntryType Type { get; set; } } -/// Result of trying to steer a queued message into a live turn. +/// 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 QueueSendNowResult +public sealed class SessionFsReaddirWithTypesResult { - /// True when the item was accepted into the steering lane; false when no main turn was live. - [JsonPropertyName("steered")] - public bool Steered { get; set; } + /// Directory entries with type information. + [JsonPropertyName("entries")] + public IList Entries { get => field ??= []; set; } + + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } } -/// Parameters for steering a queued message into a live turn. +/// 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 QueueSendNowRequest +public sealed class SessionFsReaddirWithTypesRequest { - /// 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. +/// 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 QueueHasPendingResult +public sealed class SessionFsRmRequest { - /// True when queued or immediate native work is pending. - [JsonPropertyName("hasPending")] - public bool HasPending { get; set; } + /// Ignore errors if the path does not exist. + [JsonPropertyName("force")] + public bool? Force { get; set; } + + /// Path using SessionFs conventions. + [JsonPropertyName("path")] + public string Path { get; set; } = string.Empty; + + /// Remove directories and their contents recursively. + [JsonPropertyName("recursive")] + public bool? Recursive { get; set; } + + /// Target session identifier. + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; } -/// 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 SessionQueueHasPendingRequest +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; } -/// Whether a deferred-idle drain should run. +/// Query results including rows, columns, and rows affected, or a filesystem error if execution failed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueBeginDeferredIdleDrainResult +public sealed class SessionFsSqliteQueryResult { - /// True when the host should run finishDeferredIdleDrain asynchronously. - [JsonPropertyName("shouldDrain")] - public bool ShouldDrain { get; set; } + /// Column names from the result set. + [JsonPropertyName("columns")] + public IList Columns { get => field ??= []; set; } + + /// Describes a filesystem error. + [JsonPropertyName("error")] + public SessionFsError? Error { get; set; } + + /// SQLite last_insert_rowid() value for INSERT. + [JsonPropertyName("lastInsertRowid")] + public long? LastInsertRowid { get; set; } + + /// For SELECT: array of row objects. For others: empty array. + [JsonPropertyName("rows")] + public IList> Rows { get => field ??= []; set; } + + /// Number of rows affected (for INSERT/UPDATE/DELETE). + [JsonPropertyName("rowsAffected")] + public long RowsAffected { get; set; } } -/// Inputs for starting a deferred-idle drain. +/// SQL query, query type, and optional bind parameters for executing a SQLite query against the per-session database. The provider applies its SQLite busy timeout for every call. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueBeginDeferredIdleDrainRequest +public sealed class SessionFsSqliteQueryRequest { - /// Whether the host still has active background work. - [JsonPropertyName("activeBackgroundWork")] - public bool ActiveBackgroundWork { get; set; } + /// 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; } -/// Action selected by the native deferred-idle drain. +/// Classified SQLite transaction failure. busyOrLocked guarantees rollback; postCommitAmbiguous must never be retried. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueFinishDeferredIdleDrainResult +public sealed class SessionFsSqliteTransactionError { - /// Whether the deferred idle was caused by an aborted foreground turn. - [JsonPropertyName("aborted")] - public bool Aborted { get; set; } + /// Machine-readable classification of the transaction failure. + [JsonPropertyName("errorClass")] + public SessionFsSqliteTransactionErrorClass ErrorClass { get; set; } - /// One of none, processQueue, or emitSessionIdle. - [JsonPropertyName("action")] - public string Action { get; set; } = string.Empty; + /// Human-readable transaction failure message. + [JsonPropertyName("message")] + public string Message { get; set; } = string.Empty; } -/// Inputs for completing a deferred-idle drain. +/// Per-statement results, or a classified transaction error. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueFinishDeferredIdleDrainRequest +public sealed class SessionFsSqliteTransactionResult { - /// 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; } + /// Classified transaction failure, when execution did not succeed. + [JsonPropertyName("error")] + public SessionFsSqliteTransactionError? Error { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Per-statement query results in input order. + [JsonPropertyName("results")] + public IList Results { get => field ??= []; set; } } -/// Inputs for marking session.idle deferred in native state. +/// One statement in an atomic SQLite transaction. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueDeferSessionIdleRequest +public sealed class SessionFsSqliteTransactionStatement { - /// Whether the deferred idle was caused by an aborted foreground turn. - [JsonPropertyName("aborted")] - public bool Aborted { get; set; } + /// 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; } +} +/// 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; + + /// Ordered SQL statements to execute in one transaction. + [JsonPropertyName("statements")] + public IList Statements { get => field ??= []; set; } } -/// Indicates whether a user-facing pending item was removed. +/// Indicates whether the per-session SQLite database already exists. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class QueueRemoveMostRecentResult +public sealed class SessionFsSqliteExistsResult { - /// 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; } + /// 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 SessionQueueRemoveMostRecentRequest +public sealed class SessionFsSqliteExistsRequest { /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Canvas open result returned by the provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueClearRequest +public sealed class CanvasProviderOpenResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provider-supplied status text. + [JsonPropertyName("status")] + public string? Status { get; set; } + + /// Provider-supplied title. + [JsonPropertyName("title")] + public string? Title { get; set; } + + /// URL for web-rendered canvases. + [JsonPropertyName("url")] + public string? Url { get; set; } } -/// Internal filter for consuming queued system notifications. +/// Host capabilities. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueConsumeSystemNotificationsRequest +public sealed class CanvasHostContextCapabilities { - /// Opaque runtime-owned filter object. - [JsonPropertyName("filter")] - public JsonElement Filter { get; set; } + /// Whether canvas rendering is supported. + [JsonPropertyName("canvases")] + public bool? Canvases { get; set; } +} - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; +/// 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; } } -/// Result of enqueueing the resume-pending wake item. +/// Session context supplied by the runtime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class QueueEnqueueResumePendingResult +public sealed class CanvasSessionContext { - /// True when a wake item was newly queued. - [JsonPropertyName("queued")] - public bool Queued { get; set; } + /// Active session working directory, when known. + [JsonPropertyName("workingDirectory")] + public string? WorkingDirectory { get; set; } } -/// Identifies the target session. +/// Canvas open parameters sent to the provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueEnqueueResumePendingRequest +public sealed class CanvasProviderOpenRequest { + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; + + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; + + /// Host context supplied by the runtime. + [JsonPropertyName("host")] + public CanvasHostContext? Host { get; set; } + + /// Canvas open input. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } + + /// Stable caller-supplied canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; + + /// Session context supplied by the runtime. + [JsonPropertyName("session")] + public CanvasSessionContext? Session { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Identifies the target session. +/// Canvas close parameters sent to the provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionQueueProcessRequest +public sealed class CanvasProviderCloseRequest { + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; + + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; + + /// Host context supplied by the runtime. + [JsonPropertyName("host")] + public CanvasHostContext? Host { get; set; } + + /// Canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; + + /// Session context supplied by the runtime. + [JsonPropertyName("session")] + public CanvasSessionContext? Session { get; set; } + /// Target session identifier. [JsonPropertyName("sessionId")] public string SessionId { get; set; } = string.Empty; } -/// Cursor, batch size, and optional long-poll/filter parameters for reading session events. +/// Canvas action invocation parameters sent to the provider. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class EventLogReadRequest +public sealed class CanvasProviderInvokeActionRequest { - /// 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; } + /// Action name to invoke. + [JsonPropertyName("actionName")] + public string ActionName { get; set; } = string.Empty; - /// 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; } + /// Provider-local canvas identifier. + [JsonPropertyName("canvasId")] + public string CanvasId { get; set; } = string.Empty; - /// 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; } + /// Owning provider identifier. + [JsonPropertyName("extensionId")] + public string ExtensionId { get; set; } = string.Empty; - /// 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; } + /// Host context supplied by the runtime. + [JsonPropertyName("host")] + public CanvasHostContext? Host { 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; } + /// Action input. + [JsonPropertyName("input")] + public JsonElement? Input { get; set; } - /// Maximum number of events to return in this batch (1–1000, default 200). - [JsonPropertyName("max")] - public long? Max { get; set; } + /// Canvas instance identifier. + [JsonPropertyName("instanceId")] + public string InstanceId { get; set; } = string.Empty; + + /// Session context supplied by the runtime. + [JsonPropertyName("session")] + public CanvasSessionContext? Session { 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; } +/// 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; } - /// 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; } + /// Process exit status when available; absent for in-process listener tasks. + [JsonPropertyName("exitCode")] + public long? ExitCode { get; set; } + + /// Listener UUID. + [JsonPropertyName("hostId")] + public string HostId { get; set; } = string.Empty; + + /// Cause of termination. + [JsonPropertyName("reason")] + public HostExitReason Reason { 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). +/// Opaque integrator-owned process launch profile for one extension entrypoint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class EventLogTailResult +public sealed class ExtensionLaunchProfile { - /// 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; + /// 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; } -/// Identifies the target session. +/// The launch profile for a supported entrypoint. Omit launch when the provider does not support the entrypoint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionEventLogTailRequest +public sealed class ExtensionLaunchProviderResolveResult { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Opaque launch profile, omitted when this provider does not support the entrypoint. + [JsonPropertyName("launch")] + public ExtensionLaunchProfile? Launch { get; set; } } -/// Opaque handle representing an event-type interest registration. +/// 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 RegisterEventInterestResult +public sealed class ExtensionLaunchProviderResolveRequest { - /// 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; + /// Source-qualified extension identifier. + [JsonPropertyName("id")] + public string Id { 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; } } -/// Event type to register consumer interest for, used by runtime gating logic. +/// 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)] -internal sealed class RegisterEventInterestParams +public sealed class LlmInferenceHttpRequestStartResult { - /// 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; +} + +/// The head of an outbound model-layer HTTP request. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class LlmInferenceHttpRequestStartRequest +{ + /// 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; } + + /// 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; } + + /// HTTP request headers, preserving multiple values per name. + [JsonPropertyName("headers")] + public IDictionary> Headers { get => field ??= new Dictionary>(); set; } + + /// 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; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// HTTP method, e.g. GET, POST. + [JsonPropertyName("method")] + public string Method { 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; } -} + /// 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; } -/// Opaque handle previously returned by `registerInterest` to release. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ReleaseEventInterestParams -{ - /// Handle returned by a previous `registerInterest` call. Idempotent: releasing an unknown or already-released handle is a no-op (returns success). When the last outstanding handle for an event type is released, the runtime reverts to its 'no consumer' code path for that event type. - [JsonPropertyName("handle")] - public string Handle { get; set; } = string.Empty; + /// 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; - /// Target session identifier. + /// 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; } = string.Empty; -} + public string? SessionId { get; set; } -/// Request count and cost metrics for this model. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UsageMetricsModelMetricRequests -{ - /// User-initiated premium request cost (with multiplier applied). - [JsonPropertyName("cost")] - public double Cost { get; 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; } - /// Number of API requests made with this model. - [JsonPropertyName("count")] - public long Count { get; set; } + /// Absolute request URL. + [JsonPropertyName("url")] + public string Url { get; set; } = string.Empty; } -/// Per-model token-detail entry containing the accumulated token count for one token type. +/// 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 UsageMetricsModelMetricTokenDetail +public sealed class LlmInferenceHttpRequestChunkResult { - /// Accumulated token count for this token type. - [JsonPropertyName("tokenCount")] - public long TokenCount { get; set; } } -/// Token usage metrics for this model. +/// A request body chunk or cancellation signal. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UsageMetricsModelMetricUsage +public sealed class LlmInferenceHttpRequestChunkRequest { - /// Total tokens read from prompt cache. - [JsonPropertyName("cacheReadTokens")] - public long CacheReadTokens { get; set; } - - /// Total tokens written to prompt cache. - [JsonPropertyName("cacheWriteTokens")] - public long CacheWriteTokens { get; set; } - - /// Total input tokens consumed. - [JsonPropertyName("inputTokens")] - public long InputTokens { get; set; } - - /// Total output tokens produced. - [JsonPropertyName("outputTokens")] - public long OutputTokens { get; set; } + /// 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; } - /// Total output tokens used for reasoning. - [JsonPropertyName("reasoningTokens")] - public long? ReasoningTokens { 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; } -/// Per-model usage metrics, including request counts/costs, token usage, nano-AI units, and per-token-type details. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UsageMetricsModelMetric -{ - /// Latest known prompt-cache expiration for this model. A timestamp in the past indicates that the observed cache has expired. - [JsonPropertyName("cacheExpiresAt")] - public DateTimeOffset? CacheExpiresAt { 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; } - /// Request count and cost metrics for this model. - [JsonPropertyName("requests")] - public UsageMetricsModelMetricRequests Requests { get => field ??= new(); set; } + /// Optional human-readable reason for the cancellation, propagated for logging. + [JsonPropertyName("cancelReason")] + public string? CancelReason { get; set; } - /// Token count details per type. - [JsonPropertyName("tokenDetails")] - public IDictionary? TokenDetails { 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; - /// Accumulated nano-AI units cost for this model. - [JsonPropertyName("totalNanoAiu")] - public double? TotalNanoAiu { 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; } - /// Token usage metrics for this model. - [JsonPropertyName("usage")] - public UsageMetricsModelMetricUsage Usage { get => field ??= new(); set; } + /// Matches the requestId from the originating httpRequestStart frame. + [JsonPropertyName("requestId")] + public string RequestId { get; set; } = string.Empty; } -/// Usage attributed to one agent instance, including its identity, API duration, AI units, and per-model breakdown. +/// Client environment metadata describing the process that produced a telemetry event. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UsageMetricsAgentMetric +public sealed class GitHubTelemetryClientInfo { - /// Human-readable label for this subagent invocation, copied from the originating `subagent.started` event. For task-tool subagents this is the invocation's task description rather than the agent's configured display name, so group by `agentName` for stable per-agent labels. - [JsonPropertyName("agentDisplayName")] - public string? AgentDisplayName { get; set; } + /// Copilot CLI version string. + [JsonPropertyName("cli_version")] + public string CliVersion { get; set; } = string.Empty; - /// Configured agent name, when this is a subagent. - [JsonPropertyName("agentName")] - public string? AgentName { get; set; } + /// Name of the client application. + [JsonPropertyName("client_name")] + public string? ClientName { get; set; } - /// Per-model usage for this agent, keyed by model identifier. - [JsonPropertyName("modelMetrics")] - public IDictionary ModelMetrics { get => field ??= new Dictionary(); set; } + /// Type of client. + [JsonPropertyName("client_type")] + public string? ClientType { get; set; } - /// Time spent in model API calls by this agent, in milliseconds. - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonPropertyName("totalApiDurationMs")] - public TimeSpan TotalApiDuration { get; set; } + /// Copilot subscription plan, when known. + [JsonPropertyName("copilot_plan")] + public string? CopilotPlan { get; set; } - /// Accumulated nano-AI units cost for this agent. - [JsonPropertyName("totalNanoAiu")] - public double TotalNanoAiu { get; set; } -} + /// Number of logical CPU cores on the host. + [JsonPropertyName("cpu_count")] + public long? CpuCount { get; set; } -/// Aggregated code change metrics. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UsageMetricsCodeChanges -{ - /// Distinct file paths modified during the session. - [JsonPropertyName("filesModified")] - public IList FilesModified { get => field ??= []; set; } + /// Distinct CPU model names for the host, comma-separated. + [JsonPropertyName("cpu_model")] + public string? CpuModel { get; set; } - /// Number of distinct files modified. - [JsonPropertyName("filesModifiedCount")] - public long FilesModifiedCount { get; set; } + /// Stable machine identifier for the device. + [JsonPropertyName("dev_device_id")] + public string? DevDeviceId { get; set; } - /// Total lines of code added. - [JsonPropertyName("linesAdded")] - public long LinesAdded { get; set; } + /// Whether the user is a GitHub/Microsoft staff member. + [JsonPropertyName("is_staff")] + public bool? IsStaff { get; set; } - /// Total lines of code removed. - [JsonPropertyName("linesRemoved")] - public long LinesRemoved { get; set; } -} + /// Node.js runtime version string. + [JsonPropertyName("node_version")] + public string NodeVersion { get; set; } = string.Empty; -/// Session-wide token-detail entry containing the accumulated token count for one token type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class UsageMetricsTokenDetail -{ - /// Accumulated token count for this token type. - [JsonPropertyName("tokenCount")] - public long TokenCount { get; set; } + /// Operating system architecture (e.g. arm64, x64). + [JsonPropertyName("os_arch")] + public string OsArch { get; set; } = string.Empty; + + /// Operating system platform (e.g. darwin, linux, win32). + [JsonPropertyName("os_platform")] + public string OsPlatform { get; set; } = string.Empty; + + /// Operating system version string. + [JsonPropertyName("os_version")] + public string OsVersion { get; set; } = string.Empty; } -/// Accumulated session usage metrics, including premium request cost, token counts, model breakdown, and code-change totals. +/// 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 UsageGetMetricsResult +public sealed class GitHubTelemetryEvent { - /// Per-agent usage metrics, keyed by agent instance identifier. The main conversation uses the stable key `main`. - [JsonPropertyName("agentMetrics")] - public IDictionary? AgentMetrics { get; set; } - - /// Aggregated code change metrics. - [JsonPropertyName("codeChanges")] - public UsageMetricsCodeChanges CodeChanges { get => field ??= new(); set; } - - /// Currently active model identifier. - [JsonPropertyName("currentModel")] - public string? CurrentModel { get; set; } + /// Client environment metadata. + [JsonPropertyName("client")] + public GitHubTelemetryClientInfo? Client { get; set; } - /// Input tokens from the most recent main-agent API call. - [JsonPropertyName("lastCallInputTokens")] - public long LastCallInputTokens { get; set; } + /// Copilot tracking ID for user-level attribution. + [JsonPropertyName("copilot_tracking_id")] + public string? CopilotTrackingId { get; set; } - /// Output tokens from the most recent main-agent API call. - [JsonPropertyName("lastCallOutputTokens")] - public long LastCallOutputTokens { get; set; } + /// Timestamp when the event was created (ISO 8601 format). + [JsonPropertyName("created_at")] + public string? CreatedAt { get; set; } - /// Per-model token and request metrics, keyed by model identifier. - [JsonPropertyName("modelMetrics")] - public IDictionary ModelMetrics { get => field ??= new Dictionary(); set; } + /// Experiment assignment context. + [JsonPropertyName("exp_assignment_context")] + public string? ExpAssignmentContext { get; set; } - /// ISO 8601 timestamp when the session started. - [JsonPropertyName("sessionStartTime")] - public DateTimeOffset SessionStartTime { get; set; } + /// Feature flags enabled for this session, as a map from flag to value. + [JsonPropertyName("features")] + public IDictionary? Features { get; set; } - /// Session-wide per-token-type accumulated token counts. - [JsonPropertyName("tokenDetails")] - public IDictionary? TokenDetails { get; set; } + /// Event type/kind (e.g. get_completion_with_tools_turn, tool_call_executed). + [JsonPropertyName("kind")] + public string Kind { get; set; } = string.Empty; - /// Total time spent in model API calls (milliseconds). - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonPropertyName("totalApiDurationMs")] - public TimeSpan TotalApiDuration { get; set; } + /// Numeric metrics as a map from key to value. + [JsonPropertyName("metrics")] + public IDictionary Metrics { get => field ??= new Dictionary(); set; } - /// Session-wide accumulated nano-AI units cost. - [JsonPropertyName("totalNanoAiu")] - public double? TotalNanoAiu { get; set; } + /// Reference to the model call that produced this event. + [JsonPropertyName("model_call_id")] + public string? ModelCallId { get; set; } - /// Total user-initiated premium request cost across all models (may be fractional due to multipliers). - [JsonPropertyName("totalPremiumRequestCost")] - public double TotalPremiumRequestCost { get; set; } + /// String-valued properties as a map from key to value. + [JsonPropertyName("properties")] + public IDictionary Properties { get => field ??= new Dictionary(); set; } - /// Raw count of user-initiated API requests. - [JsonPropertyName("totalUserRequests")] - public long TotalUserRequests { get; set; } + /// Session identifier the event belongs to. + [JsonPropertyName("session_id")] + public string? SessionId { get; set; } } -/// Identifies the target session. +/// 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)] -internal sealed class SessionUsageGetMetricsRequest +public sealed class GitHubTelemetryNotification { - /// Target session identifier. + /// The telemetry event, in the runtime's native GitHub-shaped telemetry format. + [JsonPropertyName("event")] + public GitHubTelemetryEvent Event { get => field ??= new(); 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; } + + /// 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; } = string.Empty; + public string? SessionId { get; set; } } -/// Prediction result. Available results include prediction details; unavailable results include an explicit reason. +/// 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(SessionLimitPredictionResultAvailable), "available")] -[JsonDerivedType(typeof(SessionLimitPredictionResultUnavailable), "unavailable")] -public partial class SessionLimitPredictionResult +[JsonDerivedType(typeof(GitHubTokenAcquireResultToken), "token")] +[JsonDerivedType(typeof(GitHubTokenAcquireResultCancelled), "cancelled")] +public partial class GitHubTokenAcquireResult { /// The type discriminator. [JsonPropertyName("kind")] @@ -24239,1743 +27673,1546 @@ public partial class SessionLimitPredictionResult } -/// Baseline data provenance for a prediction. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionLimitPredictionBaselineData -{ - /// End of the baseline data slice. - [JsonPropertyName("windowEnd")] - public string WindowEnd { get; set; } = string.Empty; - - /// Start of the baseline data slice. - [JsonPropertyName("windowStart")] - public string WindowStart { get; set; } = string.Empty; -} - -/// Semantic usage tier and its AI-credit cap. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionLimitPredictionTierOption -{ - /// 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 token variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionLimitPredictionDetails +public partial class GitHubTokenAcquireResultToken : GitHubTokenAcquireResult { - /// Baseline data provenance. - [JsonPropertyName("baselineData")] - public SessionLimitPredictionBaselineData BaselineData { get => field ??= new(); set; } - - /// Client population used for the prediction. - [JsonPropertyName("clientType")] - public SessionLimitPredictionClientType ClientType { get; set; } - - /// Resolved model family when known. - [JsonPropertyName("family")] - public string? Family { get; set; } - - /// Model identifier used for lookup. - [JsonPropertyName("modelId")] - public string ModelId { get; set; } = string.Empty; - - /// Recommended maximum AI credits for this session. - [JsonPropertyName("recommendedCap")] - public double RecommendedCap { get; set; } - - /// Tier chosen as the recommended cap. - [JsonPropertyName("recommendedTier")] - public SessionLimitPredictionTier RecommendedTier { get; set; } + /// + [JsonIgnore] + public override string Kind => "token"; - /// Baseline fallback level used to create the prediction. - [JsonPropertyName("source")] - public SessionLimitPredictionSource Source { get; set; } + /// GitHub access token acquired by the SDK host. + [JsonPropertyName("accessToken")] + public required string AccessToken { 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; + /// 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; } - /// Ordered usage tiers and their AI-credit caps. - [JsonPropertyName("tiers")] - public IList Tiers { get => field ??= []; set; } + /// OAuth token type. Defaults to bearer when omitted. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenType")] + public string? TokenType { get; set; } } -/// The available variant of . +/// The cancelled variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionLimitPredictionResultAvailable : SessionLimitPredictionResult +public partial class GitHubTokenAcquireResultCancelled : GitHubTokenAcquireResult { /// [JsonIgnore] - public override string Kind => "available"; - - /// Predicted session limit details. - [JsonPropertyName("prediction")] - public required SessionLimitPredictionDetails Prediction { get; set; } + public override string Kind => "cancelled"; } -/// The unavailable variant of . +/// Asks the SDK client to acquire a GitHub access token from an opaque callback registration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class SessionLimitPredictionResultUnavailable : SessionLimitPredictionResult +public sealed class GitHubTokenAcquireRequest { - /// - [JsonIgnore] - public override string Kind => "unavailable"; + /// Effective GitHub host for which the callback must return a token. + [JsonPropertyName("host")] + public string Host { get; set; } = string.Empty; - /// Reason no prediction is available. + /// Why the runtime is requesting a GitHub credential. [JsonPropertyName("reason")] - public required SessionLimitPredictionUnavailableReason Reason { get; set; } -} + public GitHubTokenAcquireReason Reason { 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; } + /// Opaque identifier generated by the SDK for this callback registration. + [JsonPropertyName("registrationId")] + public string RegistrationId { get; set; } = string.Empty; - /// Optional model identifier override. If omitted, the session's current model is used. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// Session receiving the token. Absent only before a cloud session has been assigned its id. + [JsonPropertyName("sessionId")] + public string? SessionId { get; set; } } -/// RPC data type for SessionLimitPredictionPredictRequestWithSession operations. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionLimitPredictionPredictRequestWithSession +/// A response is meaningful only on the connection and request that issued its challenge. +public sealed class InstallationsConfirmResult { - /// Client type to size for. Defaults to `cli-interactive`. - [JsonPropertyName("clientType")] - public SessionLimitPredictionClientType? ClientType { get; set; } + /// Exact challenge from the request. + [JsonPropertyName("confirmationId")] + public string ConfirmationId { get; set; } = string.Empty; - /// Optional model identifier override. If omitted, the session's current model is used. - [JsonPropertyName("modelId")] - public string? ModelId { get; set; } + /// Fresh explicit user decision. There is no default. + [JsonPropertyName("decision")] + public InstallationDecision Decision { get; set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Exact review commitment from the request. + [JsonPropertyName("reviewFingerprint")] + public string ReviewFingerprint { get; set; } = string.Empty; } -/// GitHub URL for the session and a flag indicating whether remote steering is enabled. +/// Only resource kinds with an implemented installation engine have a review variant. +/// Polymorphic base type discriminated by resource. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class RemoteEnableResult +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "resource", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(InstallationReviewMcp), "mcp")] +[JsonDerivedType(typeof(InstallationReviewSkill), "skill")] +public partial class InstallationReview { - /// Whether remote steering is enabled. - [JsonPropertyName("remoteSteerable")] - public bool RemoteSteerable { get; set; } - - /// GitHub frontend URL for this session. - [Url] - [StringSyntax(StringSyntaxAttribute.Uri)] - [JsonPropertyName("url")] - public string? Url { get; set; } + /// The type discriminator. + [JsonPropertyName("resource")] + public virtual string Resource { get; set; } = string.Empty; } -/// Optional remote session mode ("off", "export", or "on"); defaults to enabling both export and remote steering. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class RemoteEnableRequest -{ - /// Per-session remote mode. "off" disables remote, "export" exports session events to GitHub without enabling remote steering, "on" enables both export and remote steering. - [JsonPropertyName("mode")] - public RemoteSessionMode? Mode { get; set; } - - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} -/// Identifies the target session. +/// 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 SessionRemoteDisableRequest +[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; } -/// 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 -{ -} -/// New remote-steerability state to persist as a `session.remote_steerable_changed` event. +/// +/// 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 RemoteNotifySteerableChangedRequest +public sealed class McpInstallationRemoteConfiguration { - /// 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; } + /// + /// 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; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// 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; } -/// Current sharing status and shareable GitHub URL for a session. +/// The install variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class VisibilityGetResult +public partial class McpInstallationReviewInstall : McpInstallationReview { - /// 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; } + /// + [JsonIgnore] + public override string Action => "install"; - /// 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; } + /// Catalogue identity retained from the bound candidate when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("catalogue")] + public InstallationCatalogueIdentity? Catalogue { 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; } -} + /// Original catalogue trust metadata, not a verification claim. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("catalogueTrust")] + public CatalogTrustSnapshot? CatalogueTrust { get; set; } -/// 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; -} + /// The configuration change for the selected alternative only. + [JsonPropertyName("configurationChange")] + public required McpPlanConfigurationChange ConfigurationChange { get; set; } -/// Effective sharing status and shareable GitHub URL after updating session visibility. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class VisibilitySetResult -{ - /// 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; } + /// + /// 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; } - /// 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; } + /// Identity from the retained plan, not caller display text. + [JsonPropertyName("identity")] + public required McpPlanResourceIdentity Identity { 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; } -} + /// Non-secret values supplied for this selected alternative. + [JsonPropertyName("inputs")] + public required IList Inputs { 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; + /// Policy decision bound to this plan. + [JsonPropertyName("policy")] + public required McpPlanPolicyResult Policy { 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; } + /// Original source identity and content commitment. + [JsonPropertyName("provenance")] + public required McpPlanProvenance Provenance { get; set; } + + /// Explicit reviewed backend selection; no backend is accessed when no secrets are supplied. + [JsonPropertyName("secretStorage")] + public required McpInstallationSecretStorage SecretStorage { 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; } } -/// Scheduled prompt entry with ID, timing (`intervalMs`, `cron`, or `at`), prompt text, recurrence, and next run time. +/// The uninstall variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ScheduleEntry +public partial class McpInstallationReviewUninstall : McpInstallationReview { - /// 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; } - - /// 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; } + /// + [JsonIgnore] + public override string Action => "uninstall"; - /// Sequential id assigned by the runtime within the session. Stable across resumes (rebuilt from the event log). - [JsonPropertyName("id")] - public long Id { get; set; } + /// Identity from the installed receipt. + [JsonPropertyName("identity")] + public required McpPlanResourceIdentity Identity { get; set; } - /// Interval between scheduled ticks, in milliseconds (relative-interval schedules). - [JsonConverter(typeof(MillisecondsTimeSpanConverter))] - [JsonPropertyName("intervalMs")] - public TimeSpan? Interval { get; set; } + /// Receipt-owned installation being removed. + [JsonPropertyName("installationId")] + public required string InstallationId { get; set; } - /// ISO 8601 timestamp when the next tick is scheduled to fire. - [JsonPropertyName("nextRunAt")] - public DateTimeOffset NextRunAt { get; set; } + /// Exact planner-owned secret slots to remove, excluding shared OAuth grants. + [JsonPropertyName("ownedSecretCount")] + public required long OwnedSecretCount { get; set; } - /// Prompt text that gets enqueued on every tick. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Current removal policy, independent of permission to activate the server. + [JsonPropertyName("policy")] + public required McpPlanPolicyResult Policy { get; set; } - /// Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`). - [JsonPropertyName("recurring")] - public bool Recurring { get; set; } + /// Shared profile authentication is deliberately retained, not pending cleanup. + [JsonPropertyName("preservesSharedAuthentication")] + public required bool PreservesSharedAuthentication { 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; } + /// Source identity and content commitment retained by the installed receipt. + [JsonPropertyName("provenance")] + public required McpPlanProvenance Provenance { get; set; } - /// IANA timezone the `cron` expression is evaluated in. - [JsonPropertyName("tz")] - public string? Tz { get; set; } -} + /// Whether uninstall restores a protected pre-install configuration. + [JsonPropertyName("restoresPreviousConfiguration")] + public required bool RestoresPreviousConfiguration { get; set; } -/// Snapshot of the currently active recurring prompts for this session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ScheduleList -{ - /// Active scheduled prompts, ordered by id. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + /// Exact destination, checked for intervening changes before mutation. + [JsonPropertyName("target")] + public required McpPlanTarget Target { get; set; } } -/// Identifies the target session. +/// The mcp variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionScheduleListRequest +public partial class InstallationReviewMcp : InstallationReview { - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + [JsonIgnore] + public override string Resource => "mcp"; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionScheduleHydrateRequest -{ - /// 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; } } -/// Whether the session currently has an active self-paced schedule. +/// The skill variant of . [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleHasSelfPacedResult +public partial class InstallationReviewSkill : InstallationReview { - /// True when at least one active schedule is self-paced. - [JsonPropertyName("hasSelfPaced")] - public bool HasSelfPaced { get; set; } -} + /// + [JsonIgnore] + public override string Resource => "skill"; -/// Identifies the target session. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class SessionScheduleHasSelfPacedRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// The exact verified Skill action and its reviewed files. + [JsonPropertyName("review")] + public required SkillInstallationReview Review { get; set; } } -/// Result of registering or re-arming a scheduled prompt. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleAddResult +/// One connection-owned, expiring request for a trusted host's explicit user decision. +public sealed class InstallationsConfirmRequest { - /// 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; } -} + /// Opaque one-use challenge. Return unchanged; never log or persist. + [JsonPropertyName("confirmationId")] + public string ConfirmationId { get; set; } = string.Empty; -/// Register a relative-interval scheduled prompt. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleAddRequest -{ - /// Optional display-only prompt label. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Original plan expiry as an ISO 8601 timestamp. Confirmation never extends it. + [JsonPropertyName("expiresAt")] + public string ExpiresAt { get; set; } = string.Empty; - /// Human-readable interval such as `30s`, `5m`, or `2h`. - [JsonPropertyName("interval")] - public string Interval { get; set; } = string.Empty; + /// Random identifier of this installation operation, not a plan handle. + [JsonPropertyName("operationId")] + public string OperationId { get; set; } = string.Empty; - /// Prompt text to enqueue when the schedule fires. - [JsonPropertyName("prompt")] - public string Prompt { 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; } - /// Whether the schedule should re-arm after each tick. Defaults to true. - [JsonPropertyName("recurring")] - public bool? Recurring { get; set; } + /// Resource-specific review to present before collecting the user's decision. + [JsonPropertyName("review")] + public InstallationReview Review { get => field ??= new(); set; } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Opaque commitment to the exact review and inputs. Return unchanged; never log. + [JsonPropertyName("reviewFingerprint")] + public string ReviewFingerprint { get; set; } = string.Empty; } -/// Register a cron scheduled prompt. +/// Closed set of public task kinds a connection can negotiate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleAddCronRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct TaskKind : IEquatable { - /// 5-field cron expression. - [JsonPropertyName("cron")] - public string Cron { get; set; } = string.Empty; + private readonly string? _value; - /// Optional display-only prompt label. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public TaskKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Prompt text to enqueue when the schedule fires. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Whether the schedule should re-arm after each tick. Defaults to true. - [JsonPropertyName("recurring")] - public bool? Recurring { get; set; } + /// 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"); - /// IANA timezone for evaluating the cron expression. - [JsonPropertyName("tz")] - public string? Tz { get; set; } -} + /// Client-owned externally executed task. + public static TaskKind Client { get; } = new("client"); -/// Register an absolute-time scheduled prompt. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleAddAtRequest -{ - /// Epoch milliseconds when the prompt should fire. - [JsonPropertyName("at")] - public long At { get; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskKind left, TaskKind right) => left.Equals(right); - /// Optional display-only prompt label. - [JsonPropertyName("displayPrompt")] - public string? DisplayPrompt { get; set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskKind left, TaskKind right) => !(left == right); - /// Prompt text to enqueue when the schedule fires. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// + public override bool Equals(object? obj) => obj is TaskKind other && Equals(other); - /// Whether the schedule should re-arm after each tick. Defaults to false. - [JsonPropertyName("recurring")] - public bool? Recurring { get; set; } + /// + public bool Equals(TaskKind 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); -/// 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; } + /// + public override string ToString() => Value; - /// Prompt text to enqueue when the schedule fires. - [JsonPropertyName("prompt")] - public string Prompt { get; set; } = string.Empty; + /// 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)); + } - /// Target session identifier. - [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)); + } + } } -/// Re-arm a self-paced scheduled prompt. + +/// GitHub Mission Control compute kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleRearmSelfPacedRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct EnvironmentKind : IEquatable { - /// Epoch milliseconds when the prompt should next fire. - [JsonPropertyName("at")] - public long At { get; set; } + private readonly string? _value; - /// Id of the self-paced scheduled prompt. - [JsonPropertyName("id")] - public long Id { get; set; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public EnvironmentKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// 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 ScheduleStopResult -{ - /// The removed entry, or omitted if no entry matched. - [JsonPropertyName("entry")] - public ScheduleEntry? Entry { get; set; } -} + /// A user-managed environment on a local machine. + public static EnvironmentKind UserLocal { get; } = new("user-local"); -/// Identifier of the scheduled prompt to remove. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -internal sealed class ScheduleStopRequest -{ - /// Id of the scheduled prompt to remove. - [JsonPropertyName("id")] - public long Id { get; set; } + /// A user-managed environment in a GitHub Codespace. + public static EnvironmentKind UserCodespace { get; } = new("user-codespace"); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// A GitHub-managed environment backed by GitHub Actions. + public static EnvironmentKind ManagedActions { get; } = new("managed-actions"); -/// 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; -} + /// A GitHub-managed sandbox environment. + public static EnvironmentKind ManagedSandbox { get; } = new("managed-sandbox"); -/// 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; + /// A GitHub-managed cloud coding agent environment. + public static EnvironmentKind ManagedCca { get; } = new("managed-cca"); - /// 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 ==(EnvironmentKind left, EnvironmentKind right) => left.Equals(right); -/// 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; } -} + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(EnvironmentKind left, EnvironmentKind right) => !(left == right); -/// 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; } + /// + public override bool Equals(object? obj) => obj is EnvironmentKind other && Equals(other); - /// Opaque token identifying this workflow execution attempt. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + /// + public bool Equals(EnvironmentKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Registered workflow name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { 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 EnvironmentKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, EnvironmentKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(EnvironmentKind)); + } + } } -/// Parameters for cooperatively aborting a workflow body. + +/// Hook event name. Discovery emits the file-configurable subset; SDK callbacks additionally support callback-only events. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class WorkflowAbortRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct HookType : IEquatable { - /// Opaque token identifying the execution attempt to abort. - [JsonPropertyName("executionToken")] - public string ExecutionToken { get; set; } = string.Empty; + private readonly string? _value; - /// Workflow run identifier. - [JsonPropertyName("runId")] - public string RunId { get; set; } = string.Empty; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public HookType(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// 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; } -} + /// Runs before a tool is invoked. + public static HookType PreToolUse { get; } = new("preToolUse"); -/// 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; + /// Runs before an MCP tool is invoked. + public static HookType PreMcpToolCall { get; } = new("preMcpToolCall"); - /// Owner-scoped task key included for correlation. - [JsonPropertyName("clientTaskId")] - public string ClientTaskId { get; set; } = string.Empty; + /// Runs after a tool completes successfully. + public static HookType PostToolUse { get; } = new("postToolUse"); - /// Canonical runtime-generated task identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + /// Runs after a tool fails. + public static HookType PostToolUseFailure { get; } = new("postToolUseFailure"); - /// Reason the runtime requests cancellation. - [JsonPropertyName("reason")] - public ClientTaskCancelReason Reason { get; set; } + /// Runs after the user submits a prompt. + public static HookType UserPromptSubmitted { get; } = new("userPromptSubmitted"); - /// Session that owns the client task. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Runs after the runtime transforms the submitted prompt for the model, before it is added to session history. + public static HookType UserPromptTransformed { get; } = new("userPromptTransformed"); -/// Describes a filesystem error. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsError -{ - /// Error classification. - [JsonPropertyName("code")] - public SessionFsErrorCode Code { get; set; } + /// Runs when a session starts. + public static HookType SessionStart { get; } = new("sessionStart"); - /// Free-form detail about the error, for logging/diagnostics. - [JsonPropertyName("message")] - public string? Message { get; set; } -} + /// Runs when a session ends. + public static HookType SessionEnd { get; } = new("sessionEnd"); -/// 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; + /// Runs after an agent result is produced. + public static HookType PostResult { get; } = new("postResult"); - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } -} + /// Runs before a pull request description is generated. + public static HookType PrePRDescription { get; } = new("prePRDescription"); + + /// Runs when the agent encounters an error. + public static HookType ErrorOccurred { get; } = new("errorOccurred"); + + /// Runs when the agent stops. + public static HookType AgentStop { get; } = new("agentStop"); + + /// Runs when a subagent starts. + public static HookType SubagentStart { get; } = new("subagentStart"); + + /// Runs when a subagent stops. + public static HookType SubagentStop { get; } = new("subagentStop"); -/// 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; + /// Runs before conversation context is compacted. + public static HookType PreCompact { get; } = new("preCompact"); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Runs when the agent requests permission. + public static HookType PermissionRequest { get; } = new("permissionRequest"); -/// File path, content to write, and optional mode for the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsWriteFileRequest -{ - /// Content to write. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// Runs when the agent emits a notification. + public static HookType Notification { get; } = new("notification"); - /// Optional POSIX-style mode for newly created files. - [JsonPropertyName("mode")] - public long? Mode { get; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(HookType left, HookType right) => left.Equals(right); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(HookType left, HookType right) => !(left == right); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public override bool Equals(object? obj) => obj is HookType other && Equals(other); -/// File path, content to append, and optional mode for the client-provided session filesystem. Implementations create parent directories as needed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsAppendFileRequest -{ - /// Content to append. - [JsonPropertyName("content")] - public string Content { get; set; } = string.Empty; + /// + public bool Equals(HookType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Optional POSIX-style mode for newly created files. - [JsonPropertyName("mode")] - public long? Mode { get; set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + public override string ToString() => Value; - /// 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 HookType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } -/// 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 void Write(Utf8JsonWriter writer, HookType value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(HookType)); + } + } } -/// Path to test for existence in the client-provided session filesystem. + +/// Configuration tier that contributed a discovered hook action. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsExistsRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct HookOrigin : 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 HookOrigin(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// Filesystem metadata for the requested path, or a filesystem error if the stat failed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsStatResult -{ - /// ISO 8601 timestamp of creation. - [JsonPropertyName("birthtime")] - public DateTimeOffset Birthtime { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } + /// Hook loaded from user settings or the user's hook directory. + public static HookOrigin User { get; } = new("user"); - /// Whether the path is a directory. - [JsonPropertyName("isDirectory")] - public bool IsDirectory { get; set; } + /// Hook loaded from repository settings or the repository hook directory. + public static HookOrigin Repository { get; } = new("repository"); - /// Whether the path is a file. - [JsonPropertyName("isFile")] - public bool IsFile { get; set; } + /// Hook provided by an enabled installed or explicit plugin. Projectless rows omit projectPath and do not expand a project directory. + public static HookOrigin Plugin { get; } = new("plugin"); - /// ISO 8601 timestamp of last modification. - [JsonPropertyName("mtime")] - public DateTimeOffset Mtime { get; set; } + /// Hook enforced by centrally managed policy. + public static HookOrigin Policy { get; } = new("policy"); - /// File size in bytes. - [JsonPropertyName("size")] - public long Size { get; set; } -} + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(HookOrigin left, HookOrigin right) => left.Equals(right); -/// Path whose metadata should be returned from the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsStatRequest -{ - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(HookOrigin left, HookOrigin right) => !(left == right); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public override bool Equals(object? obj) => obj is HookOrigin other && Equals(other); -/// 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 bool Equals(HookOrigin other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Create parent directories as needed. - [JsonPropertyName("recursive")] - public bool? Recursive { get; set; } + /// + public override string ToString() => Value; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override HookOrigin Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, HookOrigin value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(HookOrigin)); + } + } } -/// Names of entries in the requested directory, or a filesystem error if the read failed. + +/// Resolved Anthropic adaptive-thinking capability for a model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirResult +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct AdaptiveThinkingSupport : IEquatable { - /// Entry names in the directory. - [JsonPropertyName("entries")] - public IList Entries { get => field ??= []; set; } + private readonly string? _value; - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } -} + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public AdaptiveThinkingSupport(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// Directory path whose entries should be listed from the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirRequest -{ - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// The model does not accept thinking.type='adaptive'. + public static AdaptiveThinkingSupport Unsupported { get; } = new("unsupported"); -/// Directory entry returned by session filesystem `readdirWithTypes`, with name and entry type. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirWithTypesEntry -{ - /// Entry name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// The model accepts adaptive thinking but also accepts thinking.type='enabled'. + public static AdaptiveThinkingSupport Optional { get; } = new("optional"); - /// Entry type. - [JsonPropertyName("type")] - public SessionFsReaddirWithTypesEntryType Type { get; set; } -} + /// The model defaults to adaptive thinking and rejects thinking.type='enabled' with HTTP 400, but still accepts thinking.type='disabled' (e.g. opus-4.7/4.8/5, sonnet-5). + public static AdaptiveThinkingSupport Required { get; } = new("required"); -/// 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 model accepts only thinking.type='adaptive'; 'enabled', 'disabled', and an omitted thinking block all fail with HTTP 400 (e.g. fable, mythos). + public static AdaptiveThinkingSupport AdaptiveOnly { get; } = new("adaptive_only"); - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } -} + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AdaptiveThinkingSupport left, AdaptiveThinkingSupport right) => left.Equals(right); -/// Directory path whose entries (with type information) should be listed from the client-provided session filesystem. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsReaddirWithTypesRequest -{ - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AdaptiveThinkingSupport left, AdaptiveThinkingSupport right) => !(left == right); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public override bool Equals(object? obj) => obj is AdaptiveThinkingSupport other && Equals(other); -/// Path to remove from the client-provided session filesystem, with options for recursive removal and force. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsRmRequest -{ - /// Ignore errors if the path does not exist. - [JsonPropertyName("force")] - public bool? Force { get; set; } + /// + public bool Equals(AdaptiveThinkingSupport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Path using SessionFs conventions. - [JsonPropertyName("path")] - public string Path { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Remove directories and their contents recursively. - [JsonPropertyName("recursive")] - public bool? Recursive { get; set; } + /// + public override string ToString() => Value; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override AdaptiveThinkingSupport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, AdaptiveThinkingSupport value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AdaptiveThinkingSupport)); + } + } } -/// Source and destination paths for renaming or moving an entry in the client-provided session filesystem. + +/// Model capability category for grouping in the model picker. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsRenameRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ModelPickerCategory : IEquatable { - /// Destination path using SessionFs conventions. - [JsonPropertyName("dest")] - public string Dest { get; set; } = string.Empty; + private readonly string? _value; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ModelPickerCategory(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Source path using SessionFs conventions. - [JsonPropertyName("src")] - public string Src { get; set; } = string.Empty; -} + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// Query results including rows, columns, and rows affected, or a filesystem error if execution failed. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteQueryResult -{ - /// Column names from the result set. - [JsonPropertyName("columns")] - public IList Columns { get => field ??= []; set; } + /// Lightweight model category optimized for faster, lower-cost interactions. + public static ModelPickerCategory Lightweight { get; } = new("lightweight"); - /// Describes a filesystem error. - [JsonPropertyName("error")] - public SessionFsError? Error { get; set; } + /// Versatile model category suitable for a broad range of tasks. + public static ModelPickerCategory Versatile { get; } = new("versatile"); - /// SQLite last_insert_rowid() value for INSERT. - [JsonPropertyName("lastInsertRowid")] - public long? LastInsertRowid { get; set; } + /// Powerful model category optimized for complex tasks. + public static ModelPickerCategory Powerful { get; } = new("powerful"); - /// For SELECT: array of row objects. For others: empty array. - [JsonPropertyName("rows")] - public IList> Rows { get => field ??= []; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelPickerCategory left, ModelPickerCategory right) => left.Equals(right); - /// Number of rows affected (for INSERT/UPDATE/DELETE). - [JsonPropertyName("rowsAffected")] - public long RowsAffected { get; set; } -} + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelPickerCategory left, ModelPickerCategory right) => !(left == right); -/// SQL query, query type, and optional bind parameters for executing a SQLite query against the per-session database. The provider applies its SQLite busy timeout for every call. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteQueryRequest -{ - /// Optional named bind parameters. - [JsonPropertyName("params")] - public IDictionary? Params { get; set; } + /// + public override bool Equals(object? obj) => obj is ModelPickerCategory other && Equals(other); - /// SQL query to execute. - [JsonPropertyName("query")] - public string Query { get; set; } = string.Empty; + /// + public bool Equals(ModelPickerCategory other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// How to execute the query: 'exec' for DDL/multi-statement (no results), 'query' for SELECT (returns rows), 'run' for INSERT/UPDATE/DELETE (returns rowsAffected). - [JsonPropertyName("queryType")] - public SessionFsSqliteQueryType QueryType { get; set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// + public override string ToString() => Value; -/// Classified SQLite transaction failure. busyOrLocked guarantees rollback; postCommitAmbiguous must never be retried. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteTransactionError -{ - /// Machine-readable classification of the transaction failure. - [JsonPropertyName("errorClass")] - public SessionFsSqliteTransactionErrorClass ErrorClass { get; set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ModelPickerCategory Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Human-readable transaction failure message. - [JsonPropertyName("message")] - public string Message { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, ModelPickerCategory value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelPickerCategory)); + } + } } -/// Per-statement results, or a classified transaction error. + +/// Relative cost tier for token-based billing users. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteTransactionResult +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ModelPickerPriceCategory : IEquatable { - /// Classified transaction failure, when execution did not succeed. - [JsonPropertyName("error")] - public SessionFsSqliteTransactionError? Error { get; set; } + private readonly string? _value; - /// Per-statement query results in input order. - [JsonPropertyName("results")] - public IList Results { get => field ??= []; set; } -} + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ModelPickerPriceCategory(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } -/// One statement in an atomic SQLite transaction. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteTransactionStatement -{ - /// Optional named bind parameters. - [JsonPropertyName("params")] - public IDictionary? Params { get; set; } + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// SQL statement to execute. - [JsonPropertyName("query")] - public string Query { get; set; } = string.Empty; + /// Lowest relative token cost tier. + public static ModelPickerPriceCategory Low { get; } = new("low"); - /// How to execute the statement. - [JsonPropertyName("queryType")] - public SessionFsSqliteQueryType QueryType { get; set; } -} + /// Medium relative token cost tier. + public static ModelPickerPriceCategory Medium { get; } = new("medium"); -/// 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; + /// High relative token cost tier. + public static ModelPickerPriceCategory High { get; } = new("high"); - /// Ordered SQL statements to execute in one transaction. - [JsonPropertyName("statements")] - public IList Statements { get => field ??= []; set; } -} + /// Highest relative token cost tier. + public static ModelPickerPriceCategory VeryHigh { get; } = new("very_high"); -/// Indicates whether the per-session SQLite database already exists. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class SessionFsSqliteExistsResult -{ - /// Whether the session database already exists. - [JsonPropertyName("exists")] - public bool Exists { get; set; } -} + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelPickerPriceCategory left, ModelPickerPriceCategory right) => left.Equals(right); -/// Identifies the target session. -public sealed class SessionFsSqliteExistsRequest -{ - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; -} + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelPickerPriceCategory left, ModelPickerPriceCategory right) => !(left == right); -/// Canvas open result returned by the provider. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasProviderOpenResult -{ - /// Provider-supplied status text. - [JsonPropertyName("status")] - public string? Status { get; set; } + /// + public override bool Equals(object? obj) => obj is ModelPickerPriceCategory other && Equals(other); + + /// + public bool Equals(ModelPickerPriceCategory other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Provider-supplied title. - [JsonPropertyName("title")] - public string? Title { get; set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// URL for web-rendered canvases. - [JsonPropertyName("url")] - public string? Url { get; set; } -} + /// + public override string ToString() => Value; -/// Host capabilities. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasHostContextCapabilities -{ - /// Whether canvas rendering is supported. - [JsonPropertyName("canvases")] - public bool? Canvases { get; set; } -} + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ModelPickerPriceCategory Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } -/// 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 void Write(Utf8JsonWriter writer, ModelPickerPriceCategory value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelPickerPriceCategory)); + } + } } -/// 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; } -} -/// Canvas open parameters sent to the provider. +/// Current policy state for this model. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasProviderOpenRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ModelPolicyState : 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 ModelPolicyState(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 model is enabled by policy. + public static ModelPolicyState Enabled { get; } = new("enabled"); - /// Stable caller-supplied canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// The model is disabled by policy. + public static ModelPolicyState Disabled { get; } = new("disabled"); - /// Session context supplied by the runtime. - [JsonPropertyName("session")] - public CanvasSessionContext? Session { get; set; } + /// No explicit policy is configured for the model. + public static ModelPolicyState Unconfigured { get; } = new("unconfigured"); - /// 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 ==(ModelPolicyState left, ModelPolicyState right) => left.Equals(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; + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelPolicyState left, ModelPolicyState right) => !(left == right); - /// Owning provider identifier. - [JsonPropertyName("extensionId")] - public string ExtensionId { get; set; } = string.Empty; + /// + public override bool Equals(object? obj) => obj is ModelPolicyState other && Equals(other); - /// Host context supplied by the runtime. - [JsonPropertyName("host")] - public CanvasHostContext? Host { get; set; } + /// + public bool Equals(ModelPolicyState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Session context supplied by the runtime. - [JsonPropertyName("session")] - public CanvasSessionContext? Session { get; set; } + /// + public override string ToString() => Value; - /// Target session identifier. - [JsonPropertyName("sessionId")] - public string SessionId { get; set; } = string.Empty; + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ModelPolicyState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ModelPolicyState value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelPolicyState)); + } + } } -/// Canvas action invocation parameters sent to the provider. + +/// State of the persistent certificate authority of the sandbox credential proxy. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class CanvasProviderInvokeActionRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SandboxProxyCaState : 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 SandboxProxyCaState(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; } + /// This platform has no supported OS trust store. The proxy uses a per-process certificate bundle. + public static SandboxProxyCaState Unsupported { get; } = new("unsupported"); - /// Action input. - [JsonPropertyName("input")] - public JsonElement? Input { get; set; } + /// OS trust does not include the certificate authority, or none is stored. + public static SandboxProxyCaState NotInstalled { get; } = new("notInstalled"); - /// Canvas instance identifier. - [JsonPropertyName("instanceId")] - public string InstanceId { get; set; } = string.Empty; + /// OS trust includes the stored certificate authority. + public static SandboxProxyCaState Installed { get; } = new("installed"); - /// Session context supplied by the runtime. - [JsonPropertyName("session")] - public CanvasSessionContext? Session { get; set; } + /// The runtime could not read the certificate authority or the OS trust store. + public static SandboxProxyCaState Error { get; } = new("error"); - /// 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 ==(SandboxProxyCaState left, SandboxProxyCaState right) => left.Equals(right); -/// Reports a supervised listener's hosting-task termination and cleanup outcome. -public sealed class HostExitedRequest -{ - /// Explicit startup or teardown failure, when present. - [JsonPropertyName("error")] - public string? Error { get; set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SandboxProxyCaState left, SandboxProxyCaState right) => !(left == right); - /// Process exit status when available; absent for in-process listener tasks. - [JsonPropertyName("exitCode")] - public long? ExitCode { get; set; } + /// + public override bool Equals(object? obj) => obj is SandboxProxyCaState other && Equals(other); - /// Listener UUID. - [JsonPropertyName("hostId")] - public string HostId { get; set; } = string.Empty; + /// + public bool Equals(SandboxProxyCaState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Cause of termination. - [JsonPropertyName("reason")] - public HostExitReason Reason { get; set; } -} + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); -/// 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; } + /// + public override string ToString() => Value; - /// 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; } + /// 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)); + } - /// 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 void Write(Utf8JsonWriter writer, SandboxProxyCaState value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SandboxProxyCaState)); + } + } } -/// 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; } -} -/// A discovered extension entrypoint that the registered integrator may classify and resolve to an opaque launch profile. +/// Server transport type: stdio, http, sse (deprecated), or memory. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ExtensionLaunchProviderResolveRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct DiscoveredMcpServerType : IEquatable { - /// Source-qualified extension identifier. - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; + private readonly string? _value; - /// Absolute path to the discovered extension entrypoint. - [JsonPropertyName("modulePath")] - public string ModulePath { get; set; } = string.Empty; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public DiscoveredMcpServerType(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Human-readable extension name. - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Discovery source for the extension entrypoint. - [JsonPropertyName("source")] - public ExtensionSource Source { get; set; } -} + /// Server communicates over stdio with a local child process. + public static DiscoveredMcpServerType Stdio { get; } = new("stdio"); -/// 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 -{ -} + /// Server communicates over streamable HTTP. + public static DiscoveredMcpServerType Http { get; } = new("http"); -/// The head of an outbound model-layer HTTP request. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpRequestStartRequest -{ - /// 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; } + /// Server communicates over Server-Sent Events (deprecated). + public static DiscoveredMcpServerType Sse { get; } = new("sse"); - /// 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; } + /// Server is backed by an in-memory runtime implementation. + public static DiscoveredMcpServerType Memory { get; } = new("memory"); - /// HTTP request headers, preserving multiple values per name. - [JsonPropertyName("headers")] - public IDictionary> Headers { get => field ??= new Dictionary>(); set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(DiscoveredMcpServerType left, DiscoveredMcpServerType right) => left.Equals(right); - /// 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; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(DiscoveredMcpServerType left, DiscoveredMcpServerType right) => !(left == right); - /// HTTP method, e.g. GET, POST. - [JsonPropertyName("method")] - public string Method { get; set; } = string.Empty; + /// + public override bool Equals(object? obj) => obj is DiscoveredMcpServerType other && Equals(other); - /// 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; } + /// + public bool Equals(DiscoveredMcpServerType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// 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; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// 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; } + /// + public override string ToString() => Value; - /// 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; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override DiscoveredMcpServerType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Absolute request URL. - [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, DiscoveredMcpServerType value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiscoveredMcpServerType)); + } + } } -/// 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 -{ -} -/// A request body chunk or cancellation signal. +/// A wire feature a caller can require of the catalog surface, negotiated per request. A grant means the runtime understands the feature's contract, not that the deployment has enabled the operation; typed unavailable results report availability separately. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class LlmInferenceHttpRequestChunkRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct CatalogCapability : 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 CatalogCapability(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; } + /// Understands the current `application/mcp-server-card+json` media type. + public static CatalogCapability McpServerCard { get; } = new("mcp-server-card"); - /// 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; + /// Understands the legacy `application/mcp-server+json` media type. + public static CatalogCapability LegacyMcpServerCard { get; } = new("legacy-mcp-server-card"); - /// 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; } + /// Understands `application/ai-skill` candidates as discovery-only and typed non-installable. + public static CatalogCapability AiSkillDiscovery { get; } = new("ai-skill-discovery"); - /// Matches the requestId from the originating httpRequestStart frame. - [JsonPropertyName("requestId")] - public string RequestId { get; set; } = string.Empty; -} + /// 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"); -/// Client environment metadata describing the process that produced a telemetry event. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubTelemetryClientInfo -{ - /// Copilot CLI version string. - [JsonPropertyName("cli_version")] - public string CliVersion { get; set; } = string.Empty; + /// 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"); - /// Name of the client application. - [JsonPropertyName("client_name")] - public string? ClientName { get; set; } + /// Understands plans that enumerate every eligible transport rather than a single preferred one. + public static CatalogCapability MultipleTransportChoice { get; } = new("multiple-transport-choice"); - /// Type of client. - [JsonPropertyName("client_type")] - public string? ClientType { get; set; } + /// Understands explicit numbered navigation and authority-reported pagination metadata with opaque tokens. Advertised and granted only when requested. + public static CatalogCapability CatalogSearchPagination { get; } = new("catalog-search-pagination"); - /// Copilot subscription plan, when known. - [JsonPropertyName("copilot_plan")] - public string? CopilotPlan { get; set; } + /// Understands versioned candidate trust snapshots. Protocol-3 callers must require this capability before the runtime adds the optional snapshot field. + public static CatalogCapability TrustSnapshot { get; } = new("trust-snapshot"); - /// Number of logical CPU cores on the host. - [JsonPropertyName("cpu_count")] - public long? CpuCount { get; set; } + /// Understands exact candidate selection through model-safe opaque references and host-only candidate-handle hand-off. + public static CatalogCapability CatalogSelection { get; } = new("catalog-selection"); - /// Distinct CPU model names for the host, comma-separated. - [JsonPropertyName("cpu_model")] - public string? CpuModel { get; set; } + /// Requires an eligible credential for the selected GitHub.com account before search egress and prohibits client-side anonymous retry, including after HTTP 401 or 403. The credential is scoped to the fixed catalog authority without redirect forwarding. Neither a grant nor successful response proves that the authority accepted the identity or selected a particular backend. Preserve this requirement on every page and retry; callers omitting it retain optional authentication. + public static CatalogCapability CatalogSearchCredentialRequired { get; } = new("catalog-search-credential-required"); - /// Stable machine identifier for the device. - [JsonPropertyName("dev_device_id")] - public string? DevDeviceId { get; set; } + /// Captures the exact existing native session, account, host and connection for authenticated catalogue search, selection and planning. Requires catalog-search-credential-required; does not grant installation or create a session. + public static CatalogCapability CatalogSearchSessionBound { get; } = new("catalog-search-session-bound"); - /// Whether the user is a GitHub/Microsoft staff member. - [JsonPropertyName("is_staff")] - public bool? IsStaff { get; set; } + /// Understands effect-free preparation, exact human-confirmed apply and owned removal for fully resolved personal remote MCP choices without supplied inputs or configured secrets. Advertised only when the real producer and lower owned admission are linked; requires original connection and bound session authority for new work. + public static CatalogCapability McpConfirmedRemoteInstallation { get; } = new("mcp-confirmed-remote-installation"); - /// Node.js runtime version string. - [JsonPropertyName("node_version")] - public string NodeVersion { get; set; } = string.Empty; + /// 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. + public static CatalogCapability McpConfiguredRemoteInstallation { get; } = new("mcp-configured-remote-installation"); - /// Operating system architecture (e.g. arm64, x64). - [JsonPropertyName("os_arch")] - public string OsArch { get; set; } = string.Empty; + /// 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"); - /// Operating system platform (e.g. darwin, linux, win32). - [JsonPropertyName("os_platform")] - public string OsPlatform { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogCapability left, CatalogCapability right) => left.Equals(right); - /// Operating system version string. - [JsonPropertyName("os_version")] - public string OsVersion { get; set; } = string.Empty; + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogCapability left, CatalogCapability right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is CatalogCapability other && Equals(other); + + /// + public bool Equals(CatalogCapability 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 CatalogCapability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, CatalogCapability value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogCapability)); + } + } } -/// 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. + +/// Whether a planned configuration change would create or modify an entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubTelemetryEvent +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpPlanConfigurationOperation : IEquatable { - /// Client environment metadata. - [JsonPropertyName("client")] - public GitHubTelemetryClientInfo? Client { get; set; } + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpPlanConfigurationOperation(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Creates a configuration entry that does not exist yet. + public static McpPlanConfigurationOperation Add { get; } = new("add"); - /// Copilot tracking ID for user-level attribution. - [JsonPropertyName("copilot_tracking_id")] - public string? CopilotTrackingId { get; set; } + /// Modifies a configuration entry that already exists. + public static McpPlanConfigurationOperation Update { get; } = new("update"); - /// Timestamp when the event was created (ISO 8601 format). - [JsonPropertyName("created_at")] - public string? CreatedAt { get; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpPlanConfigurationOperation left, McpPlanConfigurationOperation right) => left.Equals(right); - /// Experiment assignment context. - [JsonPropertyName("exp_assignment_context")] - public string? ExpAssignmentContext { get; set; } + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpPlanConfigurationOperation left, McpPlanConfigurationOperation right) => !(left == right); - /// Feature flags enabled for this session, as a map from flag to value. - [JsonPropertyName("features")] - public IDictionary? Features { get; set; } + /// + public override bool Equals(object? obj) => obj is McpPlanConfigurationOperation other && Equals(other); - /// Event type/kind (e.g. get_completion_with_tools_turn, tool_call_executed). - [JsonPropertyName("kind")] - public string Kind { get; set; } = string.Empty; + /// + public bool Equals(McpPlanConfigurationOperation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Numeric metrics as a map from key to value. - [JsonPropertyName("metrics")] - public IDictionary Metrics { get => field ??= new Dictionary(); set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Reference to the model call that produced this event. - [JsonPropertyName("model_call_id")] - public string? ModelCallId { get; set; } + /// + public override string ToString() => Value; - /// String-valued properties as a map from key to value. - [JsonPropertyName("properties")] - public IDictionary Properties { get => field ??= new Dictionary(); set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpPlanConfigurationOperation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Session identifier the event belongs to. - [JsonPropertyName("session_id")] - public string? SessionId { get; set; } + /// + public override void Write(Utf8JsonWriter writer, McpPlanConfigurationOperation value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanConfigurationOperation)); + } + } } -/// 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. + +/// Configuration scope an MCP install plan targets. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubTelemetryNotification +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpPlanScope : IEquatable { - /// The telemetry event, in the runtime's native GitHub-shaped telemetry format. - [JsonPropertyName("event")] - public GitHubTelemetryEvent Event { get => field ??= new(); set; } + private readonly string? _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; } + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpPlanScope(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// 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; } -} + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// 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; -} + /// The user's own MCP configuration. + public static McpPlanScope User { get; } = new("user"); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpPlanScope left, McpPlanScope right) => left.Equals(right); -/// The token variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class GitHubTokenAcquireResultToken : GitHubTokenAcquireResult -{ - /// - [JsonIgnore] - public override string Kind => "token"; + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpPlanScope left, McpPlanScope right) => !(left == right); - /// GitHub access token acquired by the SDK host. - [JsonPropertyName("accessToken")] - public required string AccessToken { get; set; } + /// + public override bool Equals(object? obj) => obj is McpPlanScope other && Equals(other); - /// 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; } + /// + public bool Equals(McpPlanScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// OAuth token type. Defaults to bearer when omitted. - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - [JsonPropertyName("tokenType")] - public string? TokenType { get; set; } -} + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); -/// The cancelled variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class GitHubTokenAcquireResultCancelled : GitHubTokenAcquireResult -{ /// - [JsonIgnore] - public override string Kind => "cancelled"; + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpPlanScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, McpPlanScope value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanScope)); + } + } } -/// Asks the SDK client to acquire a GitHub access token from an opaque callback registration. + +/// What policy decided for a planned server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class GitHubTokenAcquireRequest +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpPlanPolicyDecision : IEquatable { - /// Effective GitHub host for which the callback must return a token. - [JsonPropertyName("host")] - public string Host { get; set; } = string.Empty; - - /// Why the runtime is requesting a GitHub credential. - [JsonPropertyName("reason")] - public GitHubTokenAcquireReason Reason { get; set; } + private readonly string? _value; - /// Opaque identifier generated by the SDK for this callback registration. - [JsonPropertyName("registrationId")] - public string RegistrationId { get; set; } = string.Empty; + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpPlanPolicyDecision(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Session receiving the token. Absent only before a cloud session has been assigned its id. - [JsonPropertyName("sessionId")] - public string? SessionId { get; set; } -} + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; -/// A response is meaningful only on the connection and request that issued its challenge. -public sealed class InstallationsConfirmResult -{ - /// Exact challenge from the request. - [JsonPropertyName("confirmationId")] - public string ConfirmationId { get; set; } = string.Empty; + /// Policy permits the server. + public static McpPlanPolicyDecision Allowed { get; } = new("allowed"); - /// Fresh explicit user decision. There is no default. - [JsonPropertyName("decision")] - public InstallationDecision Decision { get; set; } + /// Policy forbids the server, so the plan cannot be applied. + public static McpPlanPolicyDecision Blocked { get; } = new("blocked"); - /// Exact review commitment from the request. - [JsonPropertyName("reviewFingerprint")] - public string ReviewFingerprint { get; set; } = string.Empty; -} + /// Policy permits the server only after an explicit approval. + public static McpPlanPolicyDecision RequiresApproval { get; } = new("requires-approval"); -/// Only resource kinds with an implemented installation engine have a review variant. -/// Polymorphic base type discriminated by resource. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "resource", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(InstallationReviewMcp), "mcp")] -[JsonDerivedType(typeof(InstallationReviewSkill), "skill")] -public partial class InstallationReview -{ - /// The type discriminator. - [JsonPropertyName("resource")] - public virtual string Resource { get; set; } = string.Empty; -} + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpPlanPolicyDecision left, McpPlanPolicyDecision right) => left.Equals(right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpPlanPolicyDecision left, McpPlanPolicyDecision right) => !(left == right); -/// Safe MCP review fields. No raw card, retrieval URL, plan handle or secret value. -/// Polymorphic base type discriminated by action. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonPolymorphic( - TypeDiscriminatorPropertyName = "action", - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] -[JsonDerivedType(typeof(McpInstallationReviewInstall), "install")] -[JsonDerivedType(typeof(McpInstallationReviewUninstall), "uninstall")] -public partial class McpInstallationReview -{ - /// The type discriminator. - [JsonPropertyName("action")] - public virtual string Action { get; set; } = string.Empty; -} + /// + public override bool Equals(object? obj) => obj is McpPlanPolicyDecision other && Equals(other); + /// + public bool Equals(McpPlanPolicyDecision other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); -/// -/// Final remote configuration, not a template. The producer refuses external-value -/// expansion before presenting this review. Receipt-owned secrets appear only as -/// `${installation-secret:<id>}` references whose `<id>` matches a reviewed -/// `${secret:<id>}` placeholder; values are never included. -/// -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class McpInstallationRemoteConfiguration -{ - /// - /// Configured headers, excluding separately authorised OAuth tokens. Values may - /// contain owned secret references, never secret values. - /// - [JsonPropertyName("headers")] - public IDictionary Headers { get => field ??= new Dictionary(); set; } + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Configured tool selection, not permission to invoke those tools. - [JsonPropertyName("tools")] - public IList Tools { get => field ??= []; set; } + /// + public override string ToString() => Value; - /// Transport in the effective persisted remote configuration. - [JsonPropertyName("transport")] - public McpPlanRemoteTransport Transport { get; set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpPlanPolicyDecision Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Exact resolved endpoint, without templates or secret placeholders. - [JsonPropertyName("url")] - public string Url { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, McpPlanPolicyDecision value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanPolicyDecision)); + } + } } -/// The install variant of . + +/// Which authority produced a policy decision. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationReviewInstall : McpInstallationReview +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpPlanPolicySource : 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 McpPlanPolicySource(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; } + /// No policy applied, so the server is permitted by default. + public static McpPlanPolicySource None { get; } = new("none"); - /// - /// 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; } + /// An enterprise allowlist evaluated the server. + public static McpPlanPolicySource EnterpriseAllowlist { get; } = new("enterprise-allowlist"); - /// Identity from the retained plan, not caller display text. - [JsonPropertyName("identity")] - public required McpPlanResourceIdentity Identity { get; set; } + /// The registry the card came from evaluated the server. + public static McpPlanPolicySource RegistryPolicy { get; } = new("registry-policy"); - /// Non-secret values supplied for this selected alternative. - [JsonPropertyName("inputs")] - public required IList Inputs { get; set; } + /// Local trust settings evaluated the server. + public static McpPlanPolicySource LocalTrust { get; } = new("local-trust"); - /// 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 ==(McpPlanPolicySource left, McpPlanPolicySource 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 !=(McpPlanPolicySource left, McpPlanPolicySource 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 McpPlanPolicySource other && Equals(other); - /// Only the selected alternative is applied. - [JsonPropertyName("selectedChoice")] - public required McpPlanTransportChoice SelectedChoice { get; set; } + /// + public bool Equals(McpPlanPolicySource 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 McpPlanPolicySource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, McpPlanPolicySource value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanPolicySource)); + } + } } -/// The uninstall variant of . + +/// Canonical digest algorithm for a validated MCP card. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class McpInstallationReviewUninstall : McpInstallationReview +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct CardDigestAlgorithm : 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 CardDigestAlgorithm(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; } + /// SHA-256 over RFC 8785 canonical JSON encoded as UTF-8. + public static CardDigestAlgorithm Sha256Rfc8785 { get; } = new("sha256-rfc8785"); - /// Current removal policy, independent of permission to activate the server. - [JsonPropertyName("policy")] - public required McpPlanPolicyResult Policy { get; set; } + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CardDigestAlgorithm left, CardDigestAlgorithm right) => left.Equals(right); - /// 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 not equivalent. + public static bool operator !=(CardDigestAlgorithm left, CardDigestAlgorithm right) => !(left == right); - /// Source identity and content commitment retained by the installed receipt. - [JsonPropertyName("provenance")] - public required McpPlanProvenance Provenance { get; set; } + /// + public override bool Equals(object? obj) => obj is CardDigestAlgorithm other && Equals(other); - /// Whether uninstall restores a protected pre-install configuration. - [JsonPropertyName("restoresPreviousConfiguration")] - public required bool RestoresPreviousConfiguration { get; set; } + /// + public bool Equals(CardDigestAlgorithm other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Exact destination, checked for intervening changes before mutation. - [JsonPropertyName("target")] - public required McpPlanTarget Target { get; set; } -} + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); -/// The mcp variant of . -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class InstallationReviewMcp : InstallationReview -{ /// - [JsonIgnore] - public override string Resource => "mcp"; + public override string ToString() => Value; - /// The exact MCP action and its reviewed changes. - [JsonPropertyName("review")] - public required McpInstallationReview Review { get; set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override CardDigestAlgorithm Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, CardDigestAlgorithm value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CardDigestAlgorithm)); + } + } } -/// The skill variant of . + +/// JSON MCP card media type accepted for install planning. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public partial class InstallationReviewSkill : InstallationReview +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpServerCardMediaType : 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 McpServerCardMediaType(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; + + /// The current MCP server card media type. + public static McpServerCardMediaType ApplicationMcpServerCardJson { get; } = new("application/mcp-server-card+json"); + + /// The legacy MCP server card media type, accepted for compatibility. + public static McpServerCardMediaType ApplicationMcpServerJson { get; } = new("application/mcp-server+json"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpServerCardMediaType left, McpServerCardMediaType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpServerCardMediaType left, McpServerCardMediaType right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is McpServerCardMediaType other && Equals(other); - /// Original plan expiry as an ISO 8601 timestamp. Confirmation never extends it. - [JsonPropertyName("expiresAt")] - public string ExpiresAt { get; set; } = string.Empty; + /// + public bool Equals(McpServerCardMediaType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Random identifier of this installation operation, not a plan handle. - [JsonPropertyName("operationId")] - public string OperationId { get; set; } = string.Empty; + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// - /// 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; } + /// + public override string ToString() => Value; - /// Resource-specific review to present before collecting the user's decision. - [JsonPropertyName("review")] - public InstallationReview Review { get => field ??= new(); set; } + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpServerCardMediaType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Opaque commitment to the exact review and inputs. Return unchanged; never log. - [JsonPropertyName("reviewFingerprint")] - public string ReviewFingerprint { get; set; } = string.Empty; + /// + public override void Write(Utf8JsonWriter writer, McpServerCardMediaType value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpServerCardMediaType)); + } + } } -/// Closed set of public task kinds a connection can negotiate. + +/// Where a required value is applied when the planned server is launched. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct TaskKind : IEquatable +public readonly struct McpPlanValueCategory : 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 McpPlanValueCategory(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"); + /// Set as an environment variable on the launched process. + public static McpPlanValueCategory EnvironmentVariable { get; } = new("environment-variable"); - /// Runtime-owned shell task. - public static TaskKind Shell { get; } = new("shell"); + /// Passed to the runtime that launches the package. + public static McpPlanValueCategory RuntimeArgument { get; } = new("runtime-argument"); - /// Client-owned externally executed task. - public static TaskKind Client { get; } = new("client"); + /// Passed to the packaged server itself. + public static McpPlanValueCategory PackageArgument { get; } = new("package-argument"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskKind left, TaskKind right) => left.Equals(right); + /// Sent as a request header to a remote endpoint. + public static McpPlanValueCategory Header { get; } = new("header"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskKind left, TaskKind right) => !(left == right); + /// Substituted into the remote endpoint URL. + public static McpPlanValueCategory UrlVariable { get; } = new("url-variable"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpPlanValueCategory left, McpPlanValueCategory right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpPlanValueCategory left, McpPlanValueCategory right) => !(left == right); /// - public override bool Equals(object? obj) => obj is TaskKind other && Equals(other); + public override bool Equals(object? obj) => obj is McpPlanValueCategory other && Equals(other); /// - public bool Equals(TaskKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpPlanValueCategory other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -25983,71 +29220,68 @@ 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 McpPlanValueCategory 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, McpPlanValueCategory value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanValueCategory)); } } } -/// GitHub Mission Control compute kind. +/// Scalar type a required value must conform to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct EnvironmentKind : IEquatable +public readonly struct McpPlanScalarValueType : 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 EnvironmentKind(string value) + public McpPlanScalarValueType(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 user-managed environment on a local machine. - public static EnvironmentKind UserLocal { get; } = new("user-local"); - - /// A user-managed environment in a GitHub Codespace. - public static EnvironmentKind UserCodespace { get; } = new("user-codespace"); + /// Free text. + public static McpPlanScalarValueType String { get; } = new("string"); - /// A GitHub-managed environment backed by GitHub Actions. - public static EnvironmentKind ManagedActions { get; } = new("managed-actions"); + /// A number. + public static McpPlanScalarValueType Number { get; } = new("number"); - /// A GitHub-managed sandbox environment. - public static EnvironmentKind ManagedSandbox { get; } = new("managed-sandbox"); + /// A boolean. + public static McpPlanScalarValueType Boolean { get; } = new("boolean"); - /// A GitHub-managed cloud coding agent environment. - public static EnvironmentKind ManagedCca { get; } = new("managed-cca"); + /// A filesystem path. + public static McpPlanScalarValueType Path { get; } = new("path"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(EnvironmentKind left, EnvironmentKind right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpPlanScalarValueType left, McpPlanScalarValueType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(EnvironmentKind left, EnvironmentKind right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpPlanScalarValueType left, McpPlanScalarValueType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is EnvironmentKind other && Equals(other); + public override bool Equals(object? obj) => obj is McpPlanScalarValueType other && Equals(other); /// - public bool Equals(EnvironmentKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpPlanScalarValueType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26055,107 +29289,119 @@ public EnvironmentKind(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 EnvironmentKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpPlanScalarValueType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, EnvironmentKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpPlanScalarValueType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(EnvironmentKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanScalarValueType)); } } } -/// Hook event name. Discovery emits the file-configurable subset; SDK callbacks additionally support callback-only events. +/// Discriminator for an enumerated required value. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct HookType : IEquatable +public readonly struct McpPlanEnumValueType : 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 HookType(string value) + public McpPlanEnumValueType(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; - /// Runs before a tool is invoked. - public static HookType PreToolUse { get; } = new("preToolUse"); - - /// Runs before an MCP tool is invoked. - public static HookType PreMcpToolCall { get; } = new("preMcpToolCall"); - - /// Runs after a tool completes successfully. - public static HookType PostToolUse { get; } = new("postToolUse"); - - /// Runs after a tool fails. - public static HookType PostToolUseFailure { get; } = new("postToolUseFailure"); + /// One of a fixed, non-empty set of permitted values. + public static McpPlanEnumValueType Enum { get; } = new("enum"); - /// Runs after the user submits a prompt. - public static HookType UserPromptSubmitted { get; } = new("userPromptSubmitted"); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpPlanEnumValueType left, McpPlanEnumValueType right) => left.Equals(right); - /// Runs after the runtime transforms the submitted prompt for the model, before it is added to session history. - public static HookType UserPromptTransformed { get; } = new("userPromptTransformed"); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpPlanEnumValueType left, McpPlanEnumValueType right) => !(left == right); - /// Runs when a session starts. - public static HookType SessionStart { get; } = new("sessionStart"); + /// + public override bool Equals(object? obj) => obj is McpPlanEnumValueType other && Equals(other); - /// Runs when a session ends. - public static HookType SessionEnd { get; } = new("sessionEnd"); + /// + public bool Equals(McpPlanEnumValueType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - /// Runs after an agent result is produced. - public static HookType PostResult { get; } = new("postResult"); + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - /// Runs before a pull request description is generated. - public static HookType PrePRDescription { get; } = new("prePRDescription"); + /// + public override string ToString() => Value; - /// Runs when the agent encounters an error. - public static HookType ErrorOccurred { get; } = new("errorOccurred"); + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override McpPlanEnumValueType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } - /// Runs when the agent stops. - public static HookType AgentStop { get; } = new("agentStop"); + /// + public override void Write(Utf8JsonWriter writer, McpPlanEnumValueType value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanEnumValueType)); + } + } +} - /// Runs when a subagent starts. - public static HookType SubagentStart { get; } = new("subagentStart"); - /// Runs when a subagent stops. - public static HookType SubagentStop { get; } = new("subagentStop"); +/// Transport exposed by a locally launched package. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct McpPlanPackageTransport : IEquatable +{ + private readonly string? _value; - /// Runs before conversation context is compacted. - public static HookType PreCompact { get; } = new("preCompact"); + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public McpPlanPackageTransport(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// Runs when the agent requests permission. - public static HookType PermissionRequest { get; } = new("permissionRequest"); + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Runs when the agent emits a notification. - public static HookType Notification { get; } = new("notification"); + /// A locally launched process spoken to over standard input and output. + public static McpPlanPackageTransport Stdio { get; } = new("stdio"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(HookType left, HookType right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpPlanPackageTransport left, McpPlanPackageTransport right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(HookType left, HookType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpPlanPackageTransport left, McpPlanPackageTransport right) => !(left == right); /// - public override bool Equals(object? obj) => obj is HookType other && Equals(other); + public override bool Equals(object? obj) => obj is McpPlanPackageTransport other && Equals(other); /// - public bool Equals(HookType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpPlanPackageTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26163,68 +29409,65 @@ public HookType(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 HookType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpPlanPackageTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, HookType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpPlanPackageTransport value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(HookType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanPackageTransport)); } } } -/// Configuration tier that contributed a discovered hook action. +/// Transport exposed by a remote endpoint. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct HookOrigin : IEquatable +public readonly struct McpPlanRemoteTransport : 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 HookOrigin(string value) + public McpPlanRemoteTransport(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; - /// Hook loaded from user settings or the user's hook directory. - public static HookOrigin User { get; } = new("user"); - - /// Hook loaded from repository settings or the repository hook directory. - public static HookOrigin Repository { get; } = new("repository"); + /// An HTTP endpoint. + public static McpPlanRemoteTransport Http { get; } = new("http"); - /// Hook provided by an enabled installed or explicit plugin. Projectless rows omit projectPath and do not expand a project directory. - public static HookOrigin Plugin { get; } = new("plugin"); + /// A streamable HTTP endpoint. + public static McpPlanRemoteTransport StreamableHttp { get; } = new("streamable-http"); - /// Hook enforced by centrally managed policy. - public static HookOrigin Policy { get; } = new("policy"); + /// A server-sent events endpoint. + public static McpPlanRemoteTransport Sse { get; } = new("sse"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(HookOrigin left, HookOrigin right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpPlanRemoteTransport left, McpPlanRemoteTransport right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(HookOrigin left, HookOrigin right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpPlanRemoteTransport left, McpPlanRemoteTransport right) => !(left == right); /// - public override bool Equals(object? obj) => obj is HookOrigin other && Equals(other); + public override bool Equals(object? obj) => obj is McpPlanRemoteTransport other && Equals(other); /// - public bool Equals(HookOrigin other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpPlanRemoteTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26232,68 +29475,62 @@ public HookOrigin(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 HookOrigin Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpPlanRemoteTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, HookOrigin value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpPlanRemoteTransport value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(HookOrigin)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanRemoteTransport)); } } } -/// Resolved Anthropic adaptive-thinking capability for a model. +/// Why capability and protocol-version negotiation refused a caller. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AdaptiveThinkingSupport : IEquatable +public readonly struct CatalogNegotiationRefusedReason : 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 AdaptiveThinkingSupport(string value) + public CatalogNegotiationRefusedReason(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 model does not accept thinking.type='adaptive'. - public static AdaptiveThinkingSupport Unsupported { get; } = new("unsupported"); - - /// The model accepts adaptive thinking but also accepts thinking.type='enabled'. - public static AdaptiveThinkingSupport Optional { get; } = new("optional"); - - /// The model defaults to adaptive thinking and rejects thinking.type='enabled' with HTTP 400, but still accepts thinking.type='disabled' (e.g. opus-4.7/4.8/5, sonnet-5). - public static AdaptiveThinkingSupport Required { get; } = new("required"); + /// The caller's protocol version is below the lowest this runtime serves. + public static CatalogNegotiationRefusedReason UnsupportedProtocolVersion { get; } = new("unsupported-protocol-version"); - /// The model accepts only thinking.type='adaptive'; 'enabled', 'disabled', and an omitted thinking block all fail with HTTP 400 (e.g. fable, mythos). - public static AdaptiveThinkingSupport AdaptiveOnly { get; } = new("adaptive_only"); + /// The caller requires at least one capability this runtime cannot honour. + public static CatalogNegotiationRefusedReason UnsupportedCapability { get; } = new("unsupported-capability"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AdaptiveThinkingSupport left, AdaptiveThinkingSupport right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogNegotiationRefusedReason left, CatalogNegotiationRefusedReason right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AdaptiveThinkingSupport left, AdaptiveThinkingSupport right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogNegotiationRefusedReason left, CatalogNegotiationRefusedReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AdaptiveThinkingSupport other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogNegotiationRefusedReason other && Equals(other); /// - public bool Equals(AdaptiveThinkingSupport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogNegotiationRefusedReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26301,65 +29538,65 @@ public AdaptiveThinkingSupport(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 AdaptiveThinkingSupport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogNegotiationRefusedReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AdaptiveThinkingSupport value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogNegotiationRefusedReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AdaptiveThinkingSupport)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogNegotiationRefusedReason)); } } } -/// Model capability category for grouping in the model picker. +/// Which kind of opaque handle was presented. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ModelPickerCategory : IEquatable +public readonly struct CatalogHandleType : 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 ModelPickerCategory(string value) + public CatalogHandleType(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; - /// Lightweight model category optimized for faster, lower-cost interactions. - public static ModelPickerCategory Lightweight { get; } = new("lightweight"); + /// A search candidate handle. + public static CatalogHandleType Candidate { get; } = new("candidate"); - /// Versatile model category suitable for a broad range of tasks. - public static ModelPickerCategory Versatile { get; } = new("versatile"); + /// An install plan handle. + public static CatalogHandleType Plan { get; } = new("plan"); - /// Powerful model category optimized for complex tasks. - public static ModelPickerCategory Powerful { get; } = new("powerful"); + /// A model-safe reference to one retained search candidate. + public static CatalogHandleType Selection { get; } = new("selection"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ModelPickerCategory left, ModelPickerCategory right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogHandleType left, CatalogHandleType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ModelPickerCategory left, ModelPickerCategory right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogHandleType left, CatalogHandleType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ModelPickerCategory other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogHandleType other && Equals(other); /// - public bool Equals(ModelPickerCategory other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogHandleType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26367,68 +29604,74 @@ public ModelPickerCategory(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 ModelPickerCategory Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogHandleType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ModelPickerCategory value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogHandleType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelPickerCategory)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogHandleType)); } } } -/// Relative cost tier for token-based billing users. +/// Why a presented handle was rejected. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ModelPickerPriceCategory : IEquatable +public readonly struct CatalogHandleRejectionReason : 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 ModelPickerPriceCategory(string value) + public CatalogHandleRejectionReason(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; - /// Lowest relative token cost tier. - public static ModelPickerPriceCategory Low { get; } = new("low"); + /// The handle is unparseable or unknown. + public static CatalogHandleRejectionReason Invalid { get; } = new("invalid"); - /// Medium relative token cost tier. - public static ModelPickerPriceCategory Medium { get; } = new("medium"); + /// The handle's time to live has elapsed. + public static CatalogHandleRejectionReason Stale { get; } = new("stale"); - /// High relative token cost tier. - public static ModelPickerPriceCategory High { get; } = new("high"); + /// The handle has already been used, and handles are single-use. + public static CatalogHandleRejectionReason Replayed { get; } = new("replayed"); - /// Highest relative token cost tier. - public static ModelPickerPriceCategory VeryHigh { get; } = new("very_high"); + /// The handle was issued by a different runtime instance or session. + public static CatalogHandleRejectionReason Foreign { get; } = new("foreign"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ModelPickerPriceCategory left, ModelPickerPriceCategory right) => left.Equals(right); + /// The handle was issued for another catalog operation. + public static CatalogHandleRejectionReason WrongKind { get; } = new("wrong-kind"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ModelPickerPriceCategory left, ModelPickerPriceCategory right) => !(left == right); + /// The supplied search identifier does not match the retained candidate. + public static CatalogHandleRejectionReason SearchMismatch { get; } = new("search-mismatch"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogHandleRejectionReason left, CatalogHandleRejectionReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogHandleRejectionReason left, CatalogHandleRejectionReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ModelPickerPriceCategory other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogHandleRejectionReason other && Equals(other); /// - public bool Equals(ModelPickerPriceCategory other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogHandleRejectionReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26436,65 +29679,92 @@ public ModelPickerPriceCategory(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 ModelPickerPriceCategory Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogHandleRejectionReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ModelPickerPriceCategory value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogHandleRejectionReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelPickerPriceCategory)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogHandleRejectionReason)); } } } -/// Current policy state for this model. +/// Which request field was rejected locally or by the catalog authority. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ModelPolicyState : IEquatable +public readonly struct CatalogInvalidRequestField : 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 ModelPolicyState(string value) + public CatalogInvalidRequestField(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 model is enabled by policy. - public static ModelPolicyState Enabled { get; } = new("enabled"); + /// The selected existing attached session was missing, malformed or unavailable. + public static CatalogInvalidRequestField PolicySessionId { get; } = new("policySessionId"); - /// The model is disabled by policy. - public static ModelPolicyState Disabled { get; } = new("disabled"); + /// The search query was empty or longer than permitted. + public static CatalogInvalidRequestField Query { get; } = new("query"); + + /// The requested result count fell outside its permitted range. + public static CatalogInvalidRequestField Limit { get; } = new("limit"); + + /// The requested candidate kinds were empty or contained a duplicate. + public static CatalogInvalidRequestField Kinds { get; } = new("kinds"); + + /// The negotiation block was missing or malformed. + public static CatalogInvalidRequestField Contract { get; } = new("contract"); + + /// The plan source was missing or malformed. + public static CatalogInvalidRequestField Source { get; } = new("source"); + + /// The supplied card was missing its media type, URL, or data. + public static CatalogInvalidRequestField Card { get; } = new("card"); + + /// The requested configuration scope is not one this runtime writes. + public static CatalogInvalidRequestField Scope { get; } = new("scope"); + + /// The pagination token or target was invalid, or the authority rejected the continuation. Repeat the search without page. + public static CatalogInvalidRequestField Page { get; } = new("page"); + + /// The locally owned session identifier was missing or malformed. + public static CatalogInvalidRequestField SessionId { get; } = new("sessionId"); + + /// The opaque selection reference was missing or malformed. + public static CatalogInvalidRequestField SelectionRef { get; } = new("selectionRef"); - /// No explicit policy is configured for the model. - public static ModelPolicyState Unconfigured { get; } = new("unconfigured"); + /// The terminal selection outcome was missing or unsupported. + public static CatalogInvalidRequestField Outcome { get; } = new("outcome"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ModelPolicyState left, ModelPolicyState right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogInvalidRequestField left, CatalogInvalidRequestField right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ModelPolicyState left, ModelPolicyState right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogInvalidRequestField left, CatalogInvalidRequestField right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ModelPolicyState other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogInvalidRequestField other && Equals(other); /// - public bool Equals(ModelPolicyState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogInvalidRequestField other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26502,62 +29772,65 @@ public ModelPolicyState(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 ModelPolicyState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogInvalidRequestField Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ModelPolicyState value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogInvalidRequestField value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelPolicyState)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogInvalidRequestField)); } } } -/// The neutral kind of a model provider — the model analog of `AccountKind`. A model provider is the live, entitled source a model came from; central code never branches on this beyond a single dispatch. +/// Why the catalog authority did not accept the caller's identity. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ModelProviderKind : IEquatable +public readonly struct CatalogAuthenticationRequiredReason : 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 ModelProviderKind(string value) + public CatalogAuthenticationRequiredReason(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; - /// GitHub Copilot / CAPI models, spawned by a github-resolving account that holds a Copilot seat. - public static ModelProviderKind Copilot { get; } = new("copilot"); + /// No credential was presented, so there is nothing to refresh and the caller must sign in. + public static CatalogAuthenticationRequiredReason NoCredential { get; } = new("no-credential"); + + /// A credential was presented and its lifetime has elapsed. A silent refresh is worth attempting before prompting anyone. + public static CatalogAuthenticationRequiredReason CredentialExpired { get; } = new("credential-expired"); - /// Microsoft 365 Copilot (Loki) inference models, spawned by a resolvable Entra-derived Loki account. - public static ModelProviderKind Loki { get; } = new("loki"); + /// A credential was presented and the authority refused it, for example because it was revoked, malformed, or issued for another audience. Refreshing the same rejected credential is not useful; the caller must sign in again. + public static CatalogAuthenticationRequiredReason CredentialRejected { get; } = new("credential-rejected"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ModelProviderKind left, ModelProviderKind right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogAuthenticationRequiredReason left, CatalogAuthenticationRequiredReason right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ModelProviderKind left, ModelProviderKind right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogAuthenticationRequiredReason left, CatalogAuthenticationRequiredReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ModelProviderKind other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogAuthenticationRequiredReason other && Equals(other); /// - public bool Equals(ModelProviderKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogAuthenticationRequiredReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26565,68 +29838,89 @@ public ModelProviderKind(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 ModelProviderKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogAuthenticationRequiredReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ModelProviderKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogAuthenticationRequiredReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelProviderKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAuthenticationRequiredReason)); } } } -/// Server transport type: stdio, http, sse (deprecated), or memory. +/// Categorised network failure, low cardinality so it can be aggregated without carrying a URL. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct DiscoveredMcpServerType : IEquatable +public readonly struct CatalogNetworkFailureReason : 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 DiscoveredMcpServerType(string value) + public CatalogNetworkFailureReason(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; - /// Server communicates over stdio with a local child process. - public static DiscoveredMcpServerType Stdio { get; } = new("stdio"); + /// No network is available, so nothing was attempted. + public static CatalogNetworkFailureReason Offline { get; } = new("offline"); - /// Server communicates over streamable HTTP. - public static DiscoveredMcpServerType Http { get; } = new("http"); + /// The authority's name could not be resolved. + public static CatalogNetworkFailureReason Dns { get; } = new("dns"); - /// Server communicates over Server-Sent Events (deprecated). - public static DiscoveredMcpServerType Sse { get; } = new("sse"); + /// The request exceeded its time budget. + public static CatalogNetworkFailureReason Timeout { get; } = new("timeout"); - /// Server is backed by an in-memory runtime implementation. - public static DiscoveredMcpServerType Memory { get; } = new("memory"); + /// The TLS handshake or certificate validation failed. + public static CatalogNetworkFailureReason Tls { get; } = new("tls"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(DiscoveredMcpServerType left, DiscoveredMcpServerType right) => left.Equals(right); + /// The connection was refused or reset. + public static CatalogNetworkFailureReason ConnectionRefused { get; } = new("connection-refused"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(DiscoveredMcpServerType left, DiscoveredMcpServerType right) => !(left == right); + /// The configured proxy returned 407 and requires authentication. + public static CatalogNetworkFailureReason ProxyAuthenticationRequired { get; } = new("proxy-authentication-required"); + + /// The authority rate-limited requests and supplied or implied a bounded cooldown. + public static CatalogNetworkFailureReason RateLimited { get; } = new("rate-limited"); + + /// The authority returned a transient 5xx response. + public static CatalogNetworkFailureReason ServiceUnavailable { get; } = new("service-unavailable"); + + /// The authority returned another status the runtime treats as a failure. + public static CatalogNetworkFailureReason HttpStatus { get; } = new("http-status"); + + /// The response exceeded the permitted size. + public static CatalogNetworkFailureReason ResponseTooLarge { get; } = new("response-too-large"); + + /// A redirect was refused by the runtime's redirect policy. + public static CatalogNetworkFailureReason RedirectRejected { get; } = new("redirect-rejected"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogNetworkFailureReason left, CatalogNetworkFailureReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogNetworkFailureReason left, CatalogNetworkFailureReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is DiscoveredMcpServerType other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogNetworkFailureReason other && Equals(other); /// - public bool Equals(DiscoveredMcpServerType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogNetworkFailureReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26634,98 +29928,74 @@ public DiscoveredMcpServerType(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 DiscoveredMcpServerType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogNetworkFailureReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, DiscoveredMcpServerType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogNetworkFailureReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiscoveredMcpServerType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogNetworkFailureReason)); } } } -/// A wire feature a caller can require of the catalog surface, negotiated per request. A grant means the runtime understands the feature's contract, not that the deployment has enabled the operation; typed unavailable results report availability separately. +/// Which hardened-fetch control refused a retrieval. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogCapability : IEquatable +public readonly struct CatalogUnsafeRetrievalReason : 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 CatalogCapability(string value) + public CatalogUnsafeRetrievalReason(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; - /// Understands the current `application/mcp-server-card+json` media type. - public static CatalogCapability McpServerCard { get; } = new("mcp-server-card"); - - /// Understands the legacy `application/mcp-server+json` media type. - public static CatalogCapability LegacyMcpServerCard { get; } = new("legacy-mcp-server-card"); - - /// Understands `application/ai-skill` candidates as discovery-only and typed non-installable. - public static CatalogCapability AiSkillDiscovery { get; } = new("ai-skill-discovery"); - - /// 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"); - - /// 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"); - - /// Understands plans that enumerate every eligible transport rather than a single preferred one. - public static CatalogCapability MultipleTransportChoice { get; } = new("multiple-transport-choice"); - - /// Understands explicit numbered navigation and authority-reported pagination metadata with opaque tokens. Advertised and granted only when requested. - public static CatalogCapability CatalogSearchPagination { get; } = new("catalog-search-pagination"); - - /// Understands versioned candidate trust snapshots. Protocol-3 callers must require this capability before the runtime adds the optional snapshot field. - public static CatalogCapability TrustSnapshot { get; } = new("trust-snapshot"); - - /// Understands exact candidate selection through model-safe opaque references and host-only candidate-handle hand-off. - public static CatalogCapability CatalogSelection { get; } = new("catalog-selection"); + /// The URL used a scheme the runtime refuses to fetch. + public static CatalogUnsafeRetrievalReason BlockedScheme { get; } = new("blocked-scheme"); - /// Requires an eligible credential for the selected GitHub.com account before search egress and prohibits client-side anonymous retry, including after HTTP 401 or 403. The credential is scoped to the fixed catalog authority without redirect forwarding. Neither a grant nor successful response proves that the authority accepted the identity or selected a particular backend. Preserve this requirement on every page and retry; callers omitting it retain optional authentication. - public static CatalogCapability CatalogSearchCredentialRequired { get; } = new("catalog-search-credential-required"); + /// The URL embedded credentials. + public static CatalogUnsafeRetrievalReason CredentialsInUrl { get; } = new("credentials-in-url"); - /// Captures the exact existing native session, account, host and connection for authenticated catalogue search, selection and planning. Requires catalog-search-credential-required; does not grant installation or create a session. - public static CatalogCapability CatalogSearchSessionBound { get; } = new("catalog-search-session-bound"); + /// The URL resolved to a loopback, private, link-local, or cloud metadata address. + public static CatalogUnsafeRetrievalReason BlockedAddress { get; } = new("blocked-address"); - /// Understands effect-free preparation, exact human-confirmed apply and owned removal for fully resolved personal remote MCP choices without supplied inputs or configured secrets. Advertised only when the real producer and lower owned admission are linked; requires original connection and bound session authority for new work. - public static CatalogCapability McpConfirmedRemoteInstallation { get; } = new("mcp-confirmed-remote-installation"); + /// A redirect target resolved to a blocked address. + public static CatalogUnsafeRetrievalReason RedirectToBlockedAddress { get; } = new("redirect-to-blocked-address"); - /// 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. - public static CatalogCapability McpConfiguredRemoteInstallation { get; } = new("mcp-configured-remote-installation"); + /// The configured proxy policy refused the request. + public static CatalogUnsafeRetrievalReason ProxyRejected { get; } = new("proxy-rejected"); - /// 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"); + /// The authority is not permitted for card retrieval. + public static CatalogUnsafeRetrievalReason HostNotPermitted { get; } = new("host-not-permitted"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogCapability left, CatalogCapability right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogUnsafeRetrievalReason left, CatalogUnsafeRetrievalReason right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogCapability left, CatalogCapability right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogUnsafeRetrievalReason left, CatalogUnsafeRetrievalReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogCapability other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogUnsafeRetrievalReason other && Equals(other); /// - public bool Equals(CatalogCapability other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogUnsafeRetrievalReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26733,62 +30003,68 @@ public CatalogCapability(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 CatalogCapability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogUnsafeRetrievalReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogCapability value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogUnsafeRetrievalReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogCapability)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogUnsafeRetrievalReason)); } } } -/// Whether a planned configuration change would create or modify an entry. +/// Media type a catalog card is interpreted as. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpPlanConfigurationOperation : IEquatable +public readonly struct CatalogMediaType : 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 McpPlanConfigurationOperation(string value) + public CatalogMediaType(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; - /// Creates a configuration entry that does not exist yet. - public static McpPlanConfigurationOperation Add { get; } = new("add"); + /// The current MCP server card media type. + public static CatalogMediaType ApplicationMcpServerCardJson { get; } = new("application/mcp-server-card+json"); - /// Modifies a configuration entry that already exists. - public static McpPlanConfigurationOperation Update { get; } = new("update"); + /// The legacy MCP server card media type, accepted for compatibility. + public static CatalogMediaType ApplicationMcpServerJson { get; } = new("application/mcp-server+json"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpPlanConfigurationOperation left, McpPlanConfigurationOperation right) => left.Equals(right); + /// An AI skill card. Representable and searchable, but typed non-installable. + public static CatalogMediaType ApplicationAiSkill { get; } = new("application/ai-skill"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpPlanConfigurationOperation left, McpPlanConfigurationOperation right) => !(left == right); + /// An inert Agent Plugin descriptor. + public static CatalogMediaType ApplicationVndGitHubCopilotPlugin { get; } = new("application/vnd.github.copilot-plugin"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogMediaType left, CatalogMediaType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogMediaType left, CatalogMediaType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpPlanConfigurationOperation other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogMediaType other && Equals(other); /// - public bool Equals(McpPlanConfigurationOperation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogMediaType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26796,59 +30072,71 @@ public McpPlanConfigurationOperation(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 McpPlanConfigurationOperation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogMediaType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpPlanConfigurationOperation value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogMediaType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanConfigurationOperation)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogMediaType)); } } } -/// Configuration scope an MCP install plan targets. +/// How a card failed validation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpPlanScope : IEquatable +public readonly struct CatalogMalformedCardReason : 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 McpPlanScope(string value) + public CatalogMalformedCardReason(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 user's own MCP configuration. - public static McpPlanScope User { get; } = new("user"); + /// The document is not well-formed JSON. + public static CatalogMalformedCardReason InvalidJson { get; } = new("invalid-json"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpPlanScope left, McpPlanScope right) => left.Equals(right); + /// The document does not satisfy its media type's schema. + public static CatalogMalformedCardReason SchemaViolation { get; } = new("schema-violation"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpPlanScope left, McpPlanScope right) => !(left == right); + /// The declared media type is not one this runtime understands. + public static CatalogMalformedCardReason UnsupportedMediaType { get; } = new("unsupported-media-type"); + + /// A field the media type requires is absent. + public static CatalogMalformedCardReason MissingRequiredField { get; } = new("missing-required-field"); + + /// The document exceeded the permitted size. + public static CatalogMalformedCardReason SizeLimitExceeded { get; } = new("size-limit-exceeded"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogMalformedCardReason left, CatalogMalformedCardReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogMalformedCardReason left, CatalogMalformedCardReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpPlanScope other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogMalformedCardReason other && Equals(other); /// - public bool Equals(McpPlanScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogMalformedCardReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26856,65 +30144,68 @@ public McpPlanScope(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 McpPlanScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogMalformedCardReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpPlanScope value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogMalformedCardReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanScope)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogMalformedCardReason)); } } } -/// What policy decided for a planned server. +/// Which wire-contract rule an upstream response broke. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpPlanPolicyDecision : IEquatable +public readonly struct CatalogContractViolationReason : 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 McpPlanPolicyDecision(string value) + public CatalogContractViolationReason(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; - /// Policy permits the server. - public static McpPlanPolicyDecision Allowed { get; } = new("allowed"); + /// A result carried both a URL and embedded data, when exactly one is permitted. + public static CatalogContractViolationReason BothUrlAndData { get; } = new("both-url-and-data"); - /// Policy forbids the server, so the plan cannot be applied. - public static McpPlanPolicyDecision Blocked { get; } = new("blocked"); + /// A result carried neither a URL nor embedded data, when exactly one is required. + public static CatalogContractViolationReason NeitherUrlNorData { get; } = new("neither-url-nor-data"); - /// Policy permits the server only after an explicit approval. - public static McpPlanPolicyDecision RequiresApproval { get; } = new("requires-approval"); + /// Two results claimed the same collision key: normalised identity for existing kinds, or normalised identity and declared version for Agent Plugins. + public static CatalogContractViolationReason DuplicateIdentity { get; } = new("duplicate-identity"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpPlanPolicyDecision left, McpPlanPolicyDecision right) => left.Equals(right); + /// A result declared no media type, or one this contract does not model. + public static CatalogContractViolationReason UnknownMediaType { get; } = new("unknown-media-type"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpPlanPolicyDecision left, McpPlanPolicyDecision right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogContractViolationReason left, CatalogContractViolationReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogContractViolationReason left, CatalogContractViolationReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpPlanPolicyDecision other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogContractViolationReason other && Equals(other); /// - public bool Equals(McpPlanPolicyDecision other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogContractViolationReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26922,68 +30213,65 @@ public McpPlanPolicyDecision(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 McpPlanPolicyDecision Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogContractViolationReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpPlanPolicyDecision value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogContractViolationReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanPolicyDecision)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogContractViolationReason)); } } } -/// Which authority produced a policy decision. +/// Why no usable transport could be offered. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpPlanPolicySource : IEquatable +public readonly struct CatalogUnavailableTransportReason : 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 McpPlanPolicySource(string value) + public CatalogUnavailableTransportReason(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; - /// No policy applied, so the server is permitted by default. - public static McpPlanPolicySource None { get; } = new("none"); - - /// An enterprise allowlist evaluated the server. - public static McpPlanPolicySource EnterpriseAllowlist { get; } = new("enterprise-allowlist"); + /// The card advertises no transport this runtime can use. + public static CatalogUnavailableTransportReason NoEligibleTransport { get; } = new("no-eligible-transport"); - /// The registry the card came from evaluated the server. - public static McpPlanPolicySource RegistryPolicy { get; } = new("registry-policy"); + /// Every advertised transport is of a kind this runtime does not implement. + public static CatalogUnavailableTransportReason TransportNotSupported { get; } = new("transport-not-supported"); - /// Local trust settings evaluated the server. - public static McpPlanPolicySource LocalTrust { get; } = new("local-trust"); + /// Eligible remotes could not be enumerated, so no explicit choice can be offered. + public static CatalogUnavailableTransportReason RemoteEnumerationUnavailable { get; } = new("remote-enumeration-unavailable"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpPlanPolicySource left, McpPlanPolicySource right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogUnavailableTransportReason left, CatalogUnavailableTransportReason right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpPlanPolicySource left, McpPlanPolicySource right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogUnavailableTransportReason left, CatalogUnavailableTransportReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpPlanPolicySource other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogUnavailableTransportReason other && Equals(other); /// - public bool Equals(McpPlanPolicySource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogUnavailableTransportReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -26991,59 +30279,65 @@ public McpPlanPolicySource(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 McpPlanPolicySource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogUnavailableTransportReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpPlanPolicySource value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogUnavailableTransportReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanPolicySource)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogUnavailableTransportReason)); } } } -/// Canonical digest algorithm for a validated MCP card. +/// Why a discoverable candidate cannot be installed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CardDigestAlgorithm : IEquatable +public readonly struct CatalogNotInstallableReason : 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 CardDigestAlgorithm(string value) + public CatalogNotInstallableReason(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; - /// SHA-256 over RFC 8785 canonical JSON encoded as UTF-8. - public static CardDigestAlgorithm Sha256Rfc8785 { get; } = new("sha256-rfc8785"); + /// This kind of resource is not installable through this surface. + public static CatalogNotInstallableReason KindNotInstallable { get; } = new("kind-not-installable"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CardDigestAlgorithm left, CardDigestAlgorithm right) => left.Equals(right); + /// AI skills are discoverable but have no typed importer in this phase. + public static CatalogNotInstallableReason AiSkillNotInstallable { get; } = new("ai-skill-not-installable"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CardDigestAlgorithm left, CardDigestAlgorithm right) => !(left == right); + /// Policy forbids installing this candidate. + public static CatalogNotInstallableReason PolicyForbids { get; } = new("policy-forbids"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogNotInstallableReason left, CatalogNotInstallableReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogNotInstallableReason left, CatalogNotInstallableReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CardDigestAlgorithm other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogNotInstallableReason other && Equals(other); /// - public bool Equals(CardDigestAlgorithm other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogNotInstallableReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27051,62 +30345,71 @@ public CardDigestAlgorithm(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 CardDigestAlgorithm Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogNotInstallableReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CardDigestAlgorithm value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogNotInstallableReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CardDigestAlgorithm)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogNotInstallableReason)); } } } -/// JSON MCP card media type accepted for install planning. +/// Why a catalog operation is not available on this runtime. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpServerCardMediaType : IEquatable +public readonly struct CatalogUnavailableReason : 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 McpServerCardMediaType(string value) + public CatalogUnavailableReason(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 MCP server card media type. - public static McpServerCardMediaType ApplicationMcpServerCardJson { get; } = new("application/mcp-server-card+json"); + /// Bounded search is not wired up on this runtime build. + public static CatalogUnavailableReason SearchUnavailable { get; } = new("search-unavailable"); - /// The legacy MCP server card media type, accepted for compatibility. - public static McpServerCardMediaType ApplicationMcpServerJson { get; } = new("application/mcp-server+json"); + /// Install planning is not wired up on this runtime build. + public static CatalogUnavailableReason PlanningUnavailable { get; } = new("planning-unavailable"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpServerCardMediaType left, McpServerCardMediaType right) => left.Equals(right); + /// Exact candidate selection is not available in this session or runtime. + public static CatalogUnavailableReason SelectionUnavailable { get; } = new("selection-unavailable"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpServerCardMediaType left, McpServerCardMediaType right) => !(left == right); + /// No catalog authority is configured for this runtime. + public static CatalogUnavailableReason AuthorityNotConfigured { get; } = new("authority-not-configured"); + + /// The surface is disabled by policy on this runtime. + public static CatalogUnavailableReason DisabledByPolicy { get; } = new("disabled-by-policy"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogUnavailableReason left, CatalogUnavailableReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogUnavailableReason left, CatalogUnavailableReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpServerCardMediaType other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogUnavailableReason other && Equals(other); /// - public bool Equals(McpServerCardMediaType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogUnavailableReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27114,71 +30417,68 @@ public McpServerCardMediaType(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 McpServerCardMediaType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogUnavailableReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpServerCardMediaType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogUnavailableReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpServerCardMediaType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogUnavailableReason)); } } } -/// Where a required value is applied when the planned server is launched. +/// Authority-computed exposure eligibility, kept separate from tier. The current tier-only Agent Finder response maps to `unknown`, never to a locally inferred eligibility. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpPlanValueCategory : IEquatable +public readonly struct CatalogTrustEligibility : 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 McpPlanValueCategory(string value) + public CatalogTrustEligibility(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; - /// Set as an environment variable on the launched process. - public static McpPlanValueCategory EnvironmentVariable { get; } = new("environment-variable"); - - /// Passed to the runtime that launches the package. - public static McpPlanValueCategory RuntimeArgument { get; } = new("runtime-argument"); + /// Eligible for default catalogue exposure. + public static CatalogTrustEligibility Default { get; } = new("default"); - /// Passed to the packaged server itself. - public static McpPlanValueCategory PackageArgument { get; } = new("package-argument"); + /// Eligible only when expanded or community results are requested. + public static CatalogTrustEligibility Expanded { get; } = new("expanded"); - /// Sent as a request header to a remote endpoint. - public static McpPlanValueCategory Header { get; } = new("header"); + /// Not eligible for normal catalogue exposure. + public static CatalogTrustEligibility Hidden { get; } = new("hidden"); - /// Substituted into the remote endpoint URL. - public static McpPlanValueCategory UrlVariable { get; } = new("url-variable"); + /// The authority did not supply an eligibility decision. + public static CatalogTrustEligibility Unknown { get; } = new("unknown"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpPlanValueCategory left, McpPlanValueCategory right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogTrustEligibility left, CatalogTrustEligibility right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpPlanValueCategory left, McpPlanValueCategory right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogTrustEligibility left, CatalogTrustEligibility right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpPlanValueCategory other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogTrustEligibility other && Equals(other); /// - public bool Equals(McpPlanValueCategory other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogTrustEligibility other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27186,68 +30486,59 @@ public McpPlanValueCategory(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 McpPlanValueCategory Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogTrustEligibility Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpPlanValueCategory value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogTrustEligibility value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanValueCategory)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogTrustEligibility)); } } } -/// Scalar type a required value must conform to. +/// Bounded authority that supplied a catalogue trust observation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpPlanScalarValueType : IEquatable +public readonly struct CatalogTrustSource : 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 McpPlanScalarValueType(string value) + public CatalogTrustSource(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; - /// Free text. - public static McpPlanScalarValueType String { get; } = new("string"); - - /// A number. - public static McpPlanScalarValueType Number { get; } = new("number"); - - /// A boolean. - public static McpPlanScalarValueType Boolean { get; } = new("boolean"); - - /// A filesystem path. - public static McpPlanScalarValueType Path { get; } = new("path"); + /// GitHub Agent Finder supplied the trust field on its search result. + public static CatalogTrustSource AgentFinder { get; } = new("agent-finder"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpPlanScalarValueType left, McpPlanScalarValueType right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogTrustSource left, CatalogTrustSource right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpPlanScalarValueType left, McpPlanScalarValueType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogTrustSource left, CatalogTrustSource right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpPlanScalarValueType other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogTrustSource other && Equals(other); /// - public bool Equals(McpPlanScalarValueType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogTrustSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27255,59 +30546,59 @@ public McpPlanScalarValueType(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 McpPlanScalarValueType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogTrustSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpPlanScalarValueType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogTrustSource value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanScalarValueType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogTrustSource)); } } } -/// Discriminator for an enumerated required value. +/// Schema version of the catalogue trust snapshot envelope. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpPlanEnumValueType : IEquatable +public readonly struct CatalogTrustSnapshotSchemaVersion : 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 McpPlanEnumValueType(string value) + public CatalogTrustSnapshotSchemaVersion(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; - /// One of a fixed, non-empty set of permitted values. - public static McpPlanEnumValueType Enum { get; } = new("enum"); + /// Initial envelope carrying one bounded service tier or one explicit unavailable state. + public static CatalogTrustSnapshotSchemaVersion V1 { get; } = new("v1"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpPlanEnumValueType left, McpPlanEnumValueType right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogTrustSnapshotSchemaVersion left, CatalogTrustSnapshotSchemaVersion right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpPlanEnumValueType left, McpPlanEnumValueType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogTrustSnapshotSchemaVersion left, CatalogTrustSnapshotSchemaVersion right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpPlanEnumValueType other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogTrustSnapshotSchemaVersion other && Equals(other); /// - public bool Equals(McpPlanEnumValueType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogTrustSnapshotSchemaVersion other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27315,59 +30606,62 @@ public McpPlanEnumValueType(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 McpPlanEnumValueType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogTrustSnapshotSchemaVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpPlanEnumValueType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogTrustSnapshotSchemaVersion value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanEnumValueType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogTrustSnapshotSchemaVersion)); } } } -/// Transport exposed by a locally launched package. +/// Service-computed trust tier currently emitted by Agent Finder. It is independent of search score, popularity, and client-side ranking. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpPlanPackageTransport : IEquatable +public readonly struct CatalogTrustTier : 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 McpPlanPackageTransport(string value) + public CatalogTrustTier(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 locally launched process spoken to over standard input and output. - public static McpPlanPackageTransport Stdio { get; } = new("stdio"); + /// Tier one as assigned by the catalogue authority. + public static CatalogTrustTier T1 { get; } = new("T1"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpPlanPackageTransport left, McpPlanPackageTransport right) => left.Equals(right); + /// Tier two as assigned by the catalogue authority. + public static CatalogTrustTier T2 { get; } = new("T2"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpPlanPackageTransport left, McpPlanPackageTransport right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogTrustTier left, CatalogTrustTier right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogTrustTier left, CatalogTrustTier right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpPlanPackageTransport other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogTrustTier other && Equals(other); /// - public bool Equals(McpPlanPackageTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogTrustTier other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27375,65 +30669,74 @@ public McpPlanPackageTransport(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 McpPlanPackageTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogTrustTier Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpPlanPackageTransport value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogTrustTier value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanPackageTransport)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogTrustTier)); } } } -/// Transport exposed by a remote endpoint. +/// Configuration ownership and setup observations, distinct from tool permissions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpPlanRemoteTransport : IEquatable +public readonly struct McpInstallationState : 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 McpPlanRemoteTransport(string value) + public McpInstallationState(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// An HTTP endpoint. - public static McpPlanRemoteTransport Http { get; } = new("http"); + /// Owned configuration exists; inventory alone does not grant activation. + public static McpInstallationState NeedsSetup { get; } = new("needs-setup"); - /// A streamable HTTP endpoint. - public static McpPlanRemoteTransport StreamableHttp { get; } = new("streamable-http"); + /// The selected authorised session reports an active installation. + public static McpInstallationState Active { get; } = new("active"); - /// A server-sent events endpoint. - public static McpPlanRemoteTransport Sse { get; } = new("sse"); + /// The selected server requires explicit sign-in. + public static McpInstallationState AuthenticationRequired { get; } = new("authentication-required"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpPlanRemoteTransport left, McpPlanRemoteTransport right) => left.Equals(right); + /// The selected server could not be activated. + public static McpInstallationState ActivationFailed { get; } = new("activation-failed"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpPlanRemoteTransport left, McpPlanRemoteTransport right) => !(left == right); + /// Owned configuration no longer matches its receipt. + public static McpInstallationState ConfigurationModified { get; } = new("configuration-modified"); + + /// Confirmed durable work or unsafe evidence requires recovery. + public static McpInstallationState RecoveryRequired { get; } = new("recovery-required"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpInstallationState left, McpInstallationState right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpInstallationState left, McpInstallationState right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpPlanRemoteTransport other && Equals(other); + public override bool Equals(object? obj) => obj is McpInstallationState other && Equals(other); /// - public bool Equals(McpPlanRemoteTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpInstallationState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27441,62 +30744,125 @@ public McpPlanRemoteTransport(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 McpPlanRemoteTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpInstallationState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpPlanRemoteTransport value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpInstallationState value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPlanRemoteTransport)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpInstallationState)); } } } -/// Why capability and protocol-version negotiation refused a caller. +/// Bounded refusal categories, without echoing handles, credentials or configuration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogNegotiationRefusedReason : IEquatable +public readonly struct McpInstallationFailureReason : 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 CatalogNegotiationRefusedReason(string value) + public McpInstallationFailureReason(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 caller's protocol version is below the lowest this runtime serves. - public static CatalogNegotiationRefusedReason UnsupportedProtocolVersion { get; } = new("unsupported-protocol-version"); + /// The selected choice or request is unsupported or malformed. + public static McpInstallationFailureReason InvalidRequest { get; } = new("invalid-request"); - /// The caller requires at least one capability this runtime cannot honour. - public static CatalogNegotiationRefusedReason UnsupportedCapability { get; } = new("unsupported-capability"); + /// The bounded original-connection operation limit was reached. + public static McpInstallationFailureReason OperationLimit { get; } = new("operation-limit"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogNegotiationRefusedReason left, CatalogNegotiationRefusedReason right) => left.Equals(right); + /// The original operation was cancelled. + public static McpInstallationFailureReason Cancelled { get; } = new("cancelled"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogNegotiationRefusedReason left, CatalogNegotiationRefusedReason right) => !(left == right); + /// Required installation capabilities were omitted. + public static McpInstallationFailureReason CapabilityRequired { get; } = new("capability-required"); + + /// The original host cannot receive human confirmation. + public static McpInstallationFailureReason ConfirmationUnavailable { get; } = new("confirmation-unavailable"); + + /// The confirmation response is malformed or mismatched. + public static McpInstallationFailureReason ConfirmationInvalid { get; } = new("confirmation-invalid"); + + /// Existing authenticated session and host authority is unavailable. + public static McpInstallationFailureReason PolicyContextUnavailable { get; } = new("policy-context-unavailable"); + + /// The original authority or policy changed. + public static McpInstallationFailureReason PolicyChanged { get; } = new("policy-changed"); + + /// Current managed policy refuses the operation. + public static McpInstallationFailureReason PolicyDenied { get; } = new("policy-denied"); + + /// Configuration changed after the reviewed snapshot. + public static McpInstallationFailureReason ConfigurationChanged { get; } = new("configuration-changed"); + + /// Installed configuration no longer matches ownership evidence. + public static McpInstallationFailureReason ConfigurationModified { get; } = new("configuration-modified"); + + /// No matching owned resource or original operation exists. + public static McpInstallationFailureReason ResourceNotFound { get; } = new("resource-not-found"); + + /// The original plan deadline elapsed. + public static McpInstallationFailureReason PlanExpired { get; } = new("plan-expired"); + + /// The one-use plan or prepared operation was already consumed. + public static McpInstallationFailureReason PlanReplayed { get; } = new("plan-replayed"); + + /// The handle belongs to a different runtime, session or connection. + public static McpInstallationFailureReason ForeignRuntime { get; } = new("foreign-runtime"); + + /// Fresh bound planning is required. + public static McpInstallationFailureReason ReplanRequired { get; } = new("replan-required"); + + /// Exact original source revalidation is unsupported. + public static McpInstallationFailureReason SourceRevalidationUnavailable { get; } = new("source-revalidation-unavailable"); + + /// The source differs from the retained commitment. + public static McpInstallationFailureReason SourceChanged { get; } = new("source-changed"); + + /// The original source could not be retrieved safely. + public static McpInstallationFailureReason SourceUnavailable { get; } = new("source-unavailable"); + + /// Authoritative Registry interpretation is unavailable. + public static McpInstallationFailureReason RegistryUnavailable { get; } = new("registry-unavailable"); + + /// The selected secret backend is unavailable. + public static McpInstallationFailureReason SecretStoreUnavailable { get; } = new("secret-store-unavailable"); + + /// Required owned admission and lifecycle support is absent. + public static McpInstallationFailureReason LifecycleUnavailable { get; } = new("lifecycle-unavailable"); + + /// A storage operation failed; inspect any allocated operation before retrying. + public static McpInstallationFailureReason WriteFailed { get; } = new("write-failed"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpInstallationFailureReason left, McpInstallationFailureReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpInstallationFailureReason left, McpInstallationFailureReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogNegotiationRefusedReason other && Equals(other); + public override bool Equals(object? obj) => obj is McpInstallationFailureReason other && Equals(other); /// - public bool Equals(CatalogNegotiationRefusedReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpInstallationFailureReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27504,65 +30870,62 @@ public CatalogNegotiationRefusedReason(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 CatalogNegotiationRefusedReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpInstallationFailureReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogNegotiationRefusedReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpInstallationFailureReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogNegotiationRefusedReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpInstallationFailureReason)); } } } -/// Which kind of opaque handle was presented. +/// Explicit backend selection is part of the final review; failures never switch backends. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogHandleType : IEquatable +public readonly struct McpInstallationSecretStorage : 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 CatalogHandleType(string value) + public McpInstallationSecretStorage(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 search candidate handle. - public static CatalogHandleType Candidate { get; } = new("candidate"); - - /// An install plan handle. - public static CatalogHandleType Plan { get; } = new("plan"); + /// The selected operating-system keychain, without fallback to file storage. + public static McpInstallationSecretStorage Keychain { get; } = new("keychain"); - /// A model-safe reference to one retained search candidate. - public static CatalogHandleType Selection { get; } = new("selection"); + /// The explicitly selected private file backend. + public static McpInstallationSecretStorage PrivateFile { get; } = new("private-file"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogHandleType left, CatalogHandleType right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpInstallationSecretStorage left, McpInstallationSecretStorage right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogHandleType left, CatalogHandleType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpInstallationSecretStorage left, McpInstallationSecretStorage right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogHandleType other && Equals(other); + public override bool Equals(object? obj) => obj is McpInstallationSecretStorage other && Equals(other); /// - public bool Equals(CatalogHandleType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpInstallationSecretStorage other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27570,74 +30933,62 @@ public CatalogHandleType(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 CatalogHandleType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpInstallationSecretStorage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogHandleType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpInstallationSecretStorage value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogHandleType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpInstallationSecretStorage)); } } } -/// Why a presented handle was rejected. +/// Persisted extension discovery source. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogHandleRejectionReason : IEquatable +public readonly struct DiscoveredExtensionSource : 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 CatalogHandleRejectionReason(string value) + public DiscoveredExtensionSource(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 handle is unparseable or unknown. - public static CatalogHandleRejectionReason Invalid { get; } = new("invalid"); - - /// The handle's time to live has elapsed. - public static CatalogHandleRejectionReason Stale { get; } = new("stale"); - - /// The handle has already been used, and handles are single-use. - public static CatalogHandleRejectionReason Replayed { get; } = new("replayed"); - - /// The handle was issued by a different runtime instance or session. - public static CatalogHandleRejectionReason Foreign { get; } = new("foreign"); - - /// The handle was issued for another catalog operation. - public static CatalogHandleRejectionReason WrongKind { get; } = new("wrong-kind"); + /// Extension discovered from the user's extensions directory. + public static DiscoveredExtensionSource User { get; } = new("user"); - /// The supplied search identifier does not match the retained candidate. - public static CatalogHandleRejectionReason SearchMismatch { get; } = new("search-mismatch"); + /// Extension contributed by an installed plugin. + public static DiscoveredExtensionSource Plugin { get; } = new("plugin"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogHandleRejectionReason left, CatalogHandleRejectionReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(DiscoveredExtensionSource left, DiscoveredExtensionSource right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogHandleRejectionReason left, CatalogHandleRejectionReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(DiscoveredExtensionSource left, DiscoveredExtensionSource right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogHandleRejectionReason other && Equals(other); + public override bool Equals(object? obj) => obj is DiscoveredExtensionSource other && Equals(other); /// - public bool Equals(CatalogHandleRejectionReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(DiscoveredExtensionSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27645,92 +30996,65 @@ public CatalogHandleRejectionReason(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 CatalogHandleRejectionReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override DiscoveredExtensionSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogHandleRejectionReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, DiscoveredExtensionSource value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogHandleRejectionReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiscoveredExtensionSource)); } } } -/// Which request field was rejected locally or by the catalog authority. +/// Effective extension loading and agent-management mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogInvalidRequestField : IEquatable +public readonly struct DiscoveredExtensionMode : 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 CatalogInvalidRequestField(string value) + public DiscoveredExtensionMode(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 selected existing attached session was missing, malformed or unavailable. - public static CatalogInvalidRequestField PolicySessionId { get; } = new("policySessionId"); - - /// The search query was empty or longer than permitted. - public static CatalogInvalidRequestField Query { get; } = new("query"); - - /// The requested result count fell outside its permitted range. - public static CatalogInvalidRequestField Limit { get; } = new("limit"); - - /// The requested candidate kinds were empty or contained a duplicate. - public static CatalogInvalidRequestField Kinds { get; } = new("kinds"); - - /// The negotiation block was missing or malformed. - public static CatalogInvalidRequestField Contract { get; } = new("contract"); - - /// The plan source was missing or malformed. - public static CatalogInvalidRequestField Source { get; } = new("source"); - - /// The supplied card was missing its media type, URL, or data. - public static CatalogInvalidRequestField Card { get; } = new("card"); - - /// The requested configuration scope is not one this runtime writes. - public static CatalogInvalidRequestField Scope { get; } = new("scope"); - - /// The pagination token or target was invalid, or the authority rejected the continuation. Repeat the search without page. - public static CatalogInvalidRequestField Page { get; } = new("page"); - - /// The locally owned session identifier was missing or malformed. - public static CatalogInvalidRequestField SessionId { get; } = new("sessionId"); + /// Extensions are not loaded. + public static DiscoveredExtensionMode Disabled { get; } = new("disabled"); - /// The opaque selection reference was missing or malformed. - public static CatalogInvalidRequestField SelectionRef { get; } = new("selectionRef"); + /// Extensions are loaded, but the agent cannot create, reload, or manage them. + public static DiscoveredExtensionMode LoadOnly { get; } = new("load_only"); - /// The terminal selection outcome was missing or unsupported. - public static CatalogInvalidRequestField Outcome { get; } = new("outcome"); + /// Extensions are loaded and the agent can create, reload, and manage them. + public static DiscoveredExtensionMode LoadAndAugment { get; } = new("load_and_augment"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogInvalidRequestField left, CatalogInvalidRequestField right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(DiscoveredExtensionMode left, DiscoveredExtensionMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogInvalidRequestField left, CatalogInvalidRequestField right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(DiscoveredExtensionMode left, DiscoveredExtensionMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogInvalidRequestField other && Equals(other); + public override bool Equals(object? obj) => obj is DiscoveredExtensionMode other && Equals(other); /// - public bool Equals(CatalogInvalidRequestField other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(DiscoveredExtensionMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27738,65 +31062,59 @@ public CatalogInvalidRequestField(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 CatalogInvalidRequestField Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override DiscoveredExtensionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogInvalidRequestField value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, DiscoveredExtensionMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogInvalidRequestField)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiscoveredExtensionMode)); } } } -/// Why the catalog authority did not accept the caller's identity. +/// Defines the allowed values. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogAuthenticationRequiredReason : IEquatable +public readonly struct SkillInstallationScope : 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 CatalogAuthenticationRequiredReason(string value) + public SkillInstallationScope(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// No credential was presented, so there is nothing to refresh and the caller must sign in. - public static CatalogAuthenticationRequiredReason NoCredential { get; } = new("no-credential"); + _value = value; + } - /// A credential was presented and its lifetime has elapsed. A silent refresh is worth attempting before prompting anyone. - public static CatalogAuthenticationRequiredReason CredentialExpired { get; } = new("credential-expired"); + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// A credential was presented and the authority refused it, for example because it was revoked, malformed, or issued for another audience. Refreshing the same rejected credential is not useful; the caller must sign in again. - public static CatalogAuthenticationRequiredReason CredentialRejected { get; } = new("credential-rejected"); + /// The user's personal Copilot home. + public static SkillInstallationScope Personal { get; } = new("personal"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogAuthenticationRequiredReason left, CatalogAuthenticationRequiredReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SkillInstallationScope left, SkillInstallationScope right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogAuthenticationRequiredReason left, CatalogAuthenticationRequiredReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SkillInstallationScope left, SkillInstallationScope right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogAuthenticationRequiredReason other && Equals(other); + public override bool Equals(object? obj) => obj is SkillInstallationScope other && Equals(other); /// - public bool Equals(CatalogAuthenticationRequiredReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SkillInstallationScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27804,89 +31122,65 @@ public CatalogAuthenticationRequiredReason(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 CatalogAuthenticationRequiredReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SkillInstallationScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogAuthenticationRequiredReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SkillInstallationScope value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAuthenticationRequiredReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillInstallationScope)); } } } -/// Categorised network failure, low cardinality so it can be aggregated without carrying a URL. +/// Owned Skill state observed from files and receipts. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogNetworkFailureReason : IEquatable +public readonly struct SkillInstallationOwnershipState : 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 CatalogNetworkFailureReason(string value) + public SkillInstallationOwnershipState(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; - /// No network is available, so nothing was attempted. - public static CatalogNetworkFailureReason Offline { get; } = new("offline"); - - /// The authority's name could not be resolved. - public static CatalogNetworkFailureReason Dns { get; } = new("dns"); - - /// The request exceeded its time budget. - public static CatalogNetworkFailureReason Timeout { get; } = new("timeout"); - - /// The TLS handshake or certificate validation failed. - public static CatalogNetworkFailureReason Tls { get; } = new("tls"); - - /// The connection was refused or reset. - public static CatalogNetworkFailureReason ConnectionRefused { get; } = new("connection-refused"); - - /// The configured proxy returned 407 and requires authentication. - public static CatalogNetworkFailureReason ProxyAuthenticationRequired { get; } = new("proxy-authentication-required"); - - /// The authority rate-limited requests and supplied or implied a bounded cooldown. - public static CatalogNetworkFailureReason RateLimited { get; } = new("rate-limited"); - - /// The authority returned a transient 5xx response. - public static CatalogNetworkFailureReason ServiceUnavailable { get; } = new("service-unavailable"); - - /// The authority returned another status the runtime treats as a failure. - public static CatalogNetworkFailureReason HttpStatus { get; } = new("http-status"); + /// Owned files and receipt evidence match. + public static SkillInstallationOwnershipState Intact { get; } = new("intact"); - /// The response exceeded the permitted size. - public static CatalogNetworkFailureReason ResponseTooLarge { get; } = new("response-too-large"); + /// Owned files no longer match the receipt. + public static SkillInstallationOwnershipState Modified { get; } = new("modified"); - /// A redirect was refused by the runtime's redirect policy. - public static CatalogNetworkFailureReason RedirectRejected { get; } = new("redirect-rejected"); + /// Ownership evidence requires recovery before mutation. + public static SkillInstallationOwnershipState RecoveryRequired { get; } = new("recovery-required"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogNetworkFailureReason left, CatalogNetworkFailureReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SkillInstallationOwnershipState left, SkillInstallationOwnershipState right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogNetworkFailureReason left, CatalogNetworkFailureReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SkillInstallationOwnershipState left, SkillInstallationOwnershipState right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogNetworkFailureReason other && Equals(other); + public override bool Equals(object? obj) => obj is SkillInstallationOwnershipState other && Equals(other); /// - public bool Equals(CatalogNetworkFailureReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SkillInstallationOwnershipState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27894,74 +31188,68 @@ public CatalogNetworkFailureReason(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 CatalogNetworkFailureReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SkillInstallationOwnershipState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogNetworkFailureReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SkillInstallationOwnershipState value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogNetworkFailureReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillInstallationOwnershipState)); } } } -/// Which hardened-fetch control refused a retrieval. +/// Bound-session observation after reconciling persisted enablement. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogUnsafeRetrievalReason : IEquatable +public readonly struct SkillInstallationSessionState : 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 CatalogUnsafeRetrievalReason(string value) + public SkillInstallationSessionState(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 URL used a scheme the runtime refuses to fetch. - public static CatalogUnsafeRetrievalReason BlockedScheme { get; } = new("blocked-scheme"); - - /// The URL embedded credentials. - public static CatalogUnsafeRetrievalReason CredentialsInUrl { get; } = new("credentials-in-url"); - - /// The URL resolved to a loopback, private, link-local, or cloud metadata address. - public static CatalogUnsafeRetrievalReason BlockedAddress { get; } = new("blocked-address"); + /// The selected session has loaded this Skill and it is enabled. + public static SkillInstallationSessionState LoadedEnabled { get; } = new("loaded-enabled"); - /// A redirect target resolved to a blocked address. - public static CatalogUnsafeRetrievalReason RedirectToBlockedAddress { get; } = new("redirect-to-blocked-address"); + /// The selected session has loaded this Skill or settings and it is disabled. + public static SkillInstallationSessionState LoadedDisabled { get; } = new("loaded-disabled"); - /// The configured proxy policy refused the request. - public static CatalogUnsafeRetrievalReason ProxyRejected { get; } = new("proxy-rejected"); + /// The selected session has not loaded Skills after the latest change. + public static SkillInstallationSessionState NotLoaded { get; } = new("not-loaded"); - /// The authority is not permitted for card retrieval. - public static CatalogUnsafeRetrievalReason HostNotPermitted { get; } = new("host-not-permitted"); + /// The selected session could not be inspected. + public static SkillInstallationSessionState Unknown { get; } = new("unknown"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogUnsafeRetrievalReason left, CatalogUnsafeRetrievalReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SkillInstallationSessionState left, SkillInstallationSessionState right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogUnsafeRetrievalReason left, CatalogUnsafeRetrievalReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SkillInstallationSessionState left, SkillInstallationSessionState right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogUnsafeRetrievalReason other && Equals(other); + public override bool Equals(object? obj) => obj is SkillInstallationSessionState other && Equals(other); /// - public bool Equals(CatalogUnsafeRetrievalReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SkillInstallationSessionState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -27969,68 +31257,146 @@ public CatalogUnsafeRetrievalReason(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 CatalogUnsafeRetrievalReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SkillInstallationSessionState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogUnsafeRetrievalReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SkillInstallationSessionState value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogUnsafeRetrievalReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillInstallationSessionState)); } } } -/// Media type a catalog card is interpreted as. +/// Bounded refusal categories for verified Skill installation management. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogMediaType : IEquatable +public readonly struct SkillInstallationFailureReason : 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 CatalogMediaType(string value) + public SkillInstallationFailureReason(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 MCP server card media type. - public static CatalogMediaType ApplicationMcpServerCardJson { get; } = new("application/mcp-server-card+json"); + /// The selected session does not have the Agent Finder Skill installation feature flag enabled for acquisition. + public static SkillInstallationFailureReason FeatureDisabled { get; } = new("feature-disabled"); - /// The legacy MCP server card media type, accepted for compatibility. - public static CatalogMediaType ApplicationMcpServerJson { get; } = new("application/mcp-server+json"); + /// The request is unsupported or malformed. + public static SkillInstallationFailureReason InvalidRequest { get; } = new("invalid-request"); - /// An AI skill card. Representable and searchable, but typed non-installable. - public static CatalogMediaType ApplicationAiSkill { get; } = new("application/ai-skill"); + /// The bounded operation limit was reached. + public static SkillInstallationFailureReason OperationLimit { get; } = new("operation-limit"); - /// An inert Agent Plugin descriptor. - public static CatalogMediaType ApplicationVndGitHubCopilotPlugin { get; } = new("application/vnd.github.copilot-plugin"); + /// The original operation was cancelled. + public static SkillInstallationFailureReason Cancelled { get; } = new("cancelled"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogMediaType left, CatalogMediaType right) => left.Equals(right); + /// The original host cannot receive human confirmation. + public static SkillInstallationFailureReason ConfirmationUnavailable { get; } = new("confirmation-unavailable"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogMediaType left, CatalogMediaType right) => !(left == right); + /// The confirmation response is malformed or mismatched. + public static SkillInstallationFailureReason ConfirmationInvalid { get; } = new("confirmation-invalid"); + + /// Existing authenticated session and host authority is unavailable. + public static SkillInstallationFailureReason PolicyContextUnavailable { get; } = new("policy-context-unavailable"); + + /// The original authority or policy changed. + public static SkillInstallationFailureReason PolicyChanged { get; } = new("policy-changed"); + + /// No matching owned resource or original operation exists. + public static SkillInstallationFailureReason ResourceNotFound { get; } = new("resource-not-found"); + + /// The original plan deadline elapsed. + public static SkillInstallationFailureReason PlanExpired { get; } = new("plan-expired"); + + /// The one-use plan was already consumed. + public static SkillInstallationFailureReason PlanReplayed { get; } = new("plan-replayed"); + + /// The handle belongs to a different runtime, session or connection. + public static SkillInstallationFailureReason ForeignRuntime { get; } = new("foreign-runtime"); + + /// The handle is not the expected Skill handle kind. + public static SkillInstallationFailureReason WrongKind { get; } = new("wrong-kind"); + + /// The handle was minted for a different search result or authority. + public static SkillInstallationFailureReason SearchMismatch { get; } = new("search-mismatch"); + + /// The handle or operation expired. + public static SkillInstallationFailureReason Expired { get; } = new("expired"); + + /// The candidate handle is not a verified installable Skill candidate. + public static SkillInstallationFailureReason InvalidCandidate { get; } = new("invalid-candidate"); + + /// The verified Skill descriptor could not be retrieved safely. + public static SkillInstallationFailureReason DescriptorUnavailable { get; } = new("descriptor-unavailable"); + + /// The verified Skill descriptor failed validation. + public static SkillInstallationFailureReason DescriptorInvalid { get; } = new("descriptor-invalid"); + + /// The Skill entrypoint could not be retrieved or verified. + public static SkillInstallationFailureReason EntrypointUnavailable { get; } = new("entrypoint-unavailable"); + + /// The Skill entrypoint is not a valid Skill. + public static SkillInstallationFailureReason InvalidSkill { get; } = new("invalid-skill"); + + /// The reviewed Skill payload could not be acquired. + public static SkillInstallationFailureReason PayloadUnavailable { get; } = new("payload-unavailable"); + + /// The acquired payload no longer matches the reviewed descriptor. + public static SkillInstallationFailureReason PayloadMismatch { get; } = new("payload-mismatch"); + + /// The retained Skill source changed after planning. + public static SkillInstallationFailureReason SourceChanged { get; } = new("source-changed"); + + /// The selected runtime cannot inspect or control the requested lifecycle operation. + public static SkillInstallationFailureReason LifecycleUnavailable { get; } = new("lifecycle-unavailable"); + + /// Durable Skill installation evidence requires recovery before mutation. + public static SkillInstallationFailureReason RecoveryRequired { get; } = new("recovery-required"); + + /// The Skill entrypoint exceeds the bounded complete review size. + public static SkillInstallationFailureReason ReviewTooLarge { get; } = new("review-too-large"); + + /// Skill installation storage or admission is busy. + public static SkillInstallationFailureReason Busy { get; } = new("busy"); + + /// An owned Skill with the same identity or target already exists. + public static SkillInstallationFailureReason AlreadyInstalled { get; } = new("already-installed"); + + /// Installed Skill files no longer match ownership evidence. + public static SkillInstallationFailureReason ConfigurationModified { get; } = new("configuration-modified"); + + /// A storage operation failed; inspect durable state before retrying. + public static SkillInstallationFailureReason WriteFailed { get; } = new("write-failed"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SkillInstallationFailureReason left, SkillInstallationFailureReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SkillInstallationFailureReason left, SkillInstallationFailureReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogMediaType other && Equals(other); + public override bool Equals(object? obj) => obj is SkillInstallationFailureReason other && Equals(other); /// - public bool Equals(CatalogMediaType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SkillInstallationFailureReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28038,71 +31404,68 @@ public CatalogMediaType(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 CatalogMediaType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SkillInstallationFailureReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogMediaType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SkillInstallationFailureReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogMediaType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillInstallationFailureReason)); } } } -/// How a card failed validation. +/// Which tier this directory belongs to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogMalformedCardReason : IEquatable +public readonly struct SkillDiscoveryScope : 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 CatalogMalformedCardReason(string value) + public SkillDiscoveryScope(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 document is not well-formed JSON. - public static CatalogMalformedCardReason InvalidJson { get; } = new("invalid-json"); - - /// The document does not satisfy its media type's schema. - public static CatalogMalformedCardReason SchemaViolation { get; } = new("schema-violation"); + /// A project's repository skill directory. + public static SkillDiscoveryScope Project { get; } = new("project"); - /// The declared media type is not one this runtime understands. - public static CatalogMalformedCardReason UnsupportedMediaType { get; } = new("unsupported-media-type"); + /// The user's personal Copilot skill directory. + public static SkillDiscoveryScope PersonalCopilot { get; } = new("personal-copilot"); - /// A field the media type requires is absent. - public static CatalogMalformedCardReason MissingRequiredField { get; } = new("missing-required-field"); + /// The user's personal agents skill directory. + public static SkillDiscoveryScope PersonalAgents { get; } = new("personal-agents"); - /// The document exceeded the permitted size. - public static CatalogMalformedCardReason SizeLimitExceeded { get; } = new("size-limit-exceeded"); + /// A configured custom skill directory. + public static SkillDiscoveryScope Custom { get; } = new("custom"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogMalformedCardReason left, CatalogMalformedCardReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SkillDiscoveryScope left, SkillDiscoveryScope right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogMalformedCardReason left, CatalogMalformedCardReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SkillDiscoveryScope left, SkillDiscoveryScope right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogMalformedCardReason other && Equals(other); + public override bool Equals(object? obj) => obj is SkillDiscoveryScope other && Equals(other); /// - public bool Equals(CatalogMalformedCardReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SkillDiscoveryScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28110,68 +31473,62 @@ public CatalogMalformedCardReason(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 CatalogMalformedCardReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SkillDiscoveryScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogMalformedCardReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SkillDiscoveryScope value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogMalformedCardReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillDiscoveryScope)); } } } -/// Which wire-contract rule an upstream response broke. +/// Whether an MCP server candidate can be planned for installation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogContractViolationReason : IEquatable +public readonly struct CatalogMcpServerInstallability : 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 CatalogContractViolationReason(string value) + public CatalogMcpServerInstallability(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 result carried both a URL and embedded data, when exactly one is permitted. - public static CatalogContractViolationReason BothUrlAndData { get; } = new("both-url-and-data"); - - /// A result carried neither a URL nor embedded data, when exactly one is required. - public static CatalogContractViolationReason NeitherUrlNorData { get; } = new("neither-url-nor-data"); - - /// Two results claimed the same collision key: normalised identity for existing kinds, or normalised identity and declared version for Agent Plugins. - public static CatalogContractViolationReason DuplicateIdentity { get; } = new("duplicate-identity"); + /// An install plan can be computed for this MCP server candidate. + public static CatalogMcpServerInstallability Installable { get; } = new("installable"); - /// A result declared no media type, or one this contract does not model. - public static CatalogContractViolationReason UnknownMediaType { get; } = new("unknown-media-type"); + /// Policy forbids installing this MCP server candidate. + public static CatalogMcpServerInstallability NotInstallablePolicy { get; } = new("not-installable-policy"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogContractViolationReason left, CatalogContractViolationReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogMcpServerInstallability left, CatalogMcpServerInstallability right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogContractViolationReason left, CatalogContractViolationReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogMcpServerInstallability left, CatalogMcpServerInstallability right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogContractViolationReason other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogMcpServerInstallability other && Equals(other); /// - public bool Equals(CatalogContractViolationReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogMcpServerInstallability other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28179,65 +31536,71 @@ public CatalogContractViolationReason(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 CatalogContractViolationReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogMcpServerInstallability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogContractViolationReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogMcpServerInstallability value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogContractViolationReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogMcpServerInstallability)); } } } -/// Why no usable transport could be offered. +/// Typed installability state for an AI skill candidate. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogUnavailableTransportReason : IEquatable +public readonly struct CatalogAiSkillInstallability : 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 CatalogUnavailableTransportReason(string value) + public CatalogAiSkillInstallability(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 card advertises no transport this runtime can use. - public static CatalogUnavailableTransportReason NoEligibleTransport { get; } = new("no-eligible-transport"); + /// This AI skill candidate carries verified materialisation metadata and the selected session may plan installation. + public static CatalogAiSkillInstallability Installable { get; } = new("installable"); - /// Every advertised transport is of a kind this runtime does not implement. - public static CatalogUnavailableTransportReason TransportNotSupported { get; } = new("transport-not-supported"); + /// Skill installation is understood but disabled for the selected session. + public static CatalogAiSkillInstallability FeatureDisabled { get; } = new("feature-disabled"); - /// Eligible remotes could not be enumerated, so no explicit choice can be offered. - public static CatalogUnavailableTransportReason RemoteEnumerationUnavailable { get; } = new("remote-enumeration-unavailable"); + /// The candidate lacks verified materialisation metadata required for installation. + public static CatalogAiSkillInstallability MaterialisationUnavailable { get; } = new("materialisation-unavailable"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogUnavailableTransportReason left, CatalogUnavailableTransportReason right) => left.Equals(right); + /// Policy refuses Skill installation for the selected session or authority. + public static CatalogAiSkillInstallability PolicyForbids { get; } = new("policy-forbids"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogUnavailableTransportReason left, CatalogUnavailableTransportReason right) => !(left == right); + /// Compatibility value for discovery-only callers that did not negotiate Skill installation. + public static CatalogAiSkillInstallability NotInstallableKind { get; } = new("not-installable-kind"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogAiSkillInstallability left, CatalogAiSkillInstallability right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogAiSkillInstallability left, CatalogAiSkillInstallability right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogUnavailableTransportReason other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogAiSkillInstallability other && Equals(other); /// - public bool Equals(CatalogUnavailableTransportReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogAiSkillInstallability other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28245,65 +31608,59 @@ public CatalogUnavailableTransportReason(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 CatalogUnavailableTransportReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogAiSkillInstallability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogUnavailableTransportReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogAiSkillInstallability value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogUnavailableTransportReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAiSkillInstallability)); } } } -/// Why a discoverable candidate cannot be installed. +/// Canonical AI skill media type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogNotInstallableReason : IEquatable +public readonly struct CatalogAiSkillMediaType : 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 CatalogNotInstallableReason(string value) + public CatalogAiSkillMediaType(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 kind of resource is not installable through this surface. - public static CatalogNotInstallableReason KindNotInstallable { get; } = new("kind-not-installable"); - - /// AI skills are discoverable but have no typed importer in this phase. - public static CatalogNotInstallableReason AiSkillNotInstallable { get; } = new("ai-skill-not-installable"); - - /// Policy forbids installing this candidate. - public static CatalogNotInstallableReason PolicyForbids { get; } = new("policy-forbids"); + /// An AI skill card. + public static CatalogAiSkillMediaType ApplicationAiSkill { get; } = new("application/ai-skill"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogNotInstallableReason left, CatalogNotInstallableReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogAiSkillMediaType left, CatalogAiSkillMediaType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogNotInstallableReason left, CatalogNotInstallableReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogAiSkillMediaType left, CatalogAiSkillMediaType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogNotInstallableReason other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogAiSkillMediaType other && Equals(other); /// - public bool Equals(CatalogNotInstallableReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogAiSkillMediaType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28311,71 +31668,65 @@ public CatalogNotInstallableReason(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 CatalogNotInstallableReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogAiSkillMediaType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogNotInstallableReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogAiSkillMediaType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogNotInstallableReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAiSkillMediaType)); } } } -/// Why a catalog operation is not available on this runtime. +/// Explicit Agent Plugin compatibility declared by exact catalog tags. Clients must not infer these values from display text or other metadata. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogUnavailableReason : IEquatable +public readonly struct CatalogAgentPluginCompatibilityTag : 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 CatalogUnavailableReason(string value) + public CatalogAgentPluginCompatibilityTag(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; - /// Bounded search is not wired up on this runtime build. - public static CatalogUnavailableReason SearchUnavailable { get; } = new("search-unavailable"); - - /// Install planning is not wired up on this runtime build. - public static CatalogUnavailableReason PlanningUnavailable { get; } = new("planning-unavailable"); - - /// Exact candidate selection is not available in this session or runtime. - public static CatalogUnavailableReason SelectionUnavailable { get; } = new("selection-unavailable"); - - /// No catalog authority is configured for this runtime. - public static CatalogUnavailableReason AuthorityNotConfigured { get; } = new("authority-not-configured"); + /// The plugin contributes at least one GitHub Copilot Canvas. + public static CatalogAgentPluginCompatibilityTag Canvas { get; } = new("canvas"); - /// The surface is disabled by policy on this runtime. - public static CatalogUnavailableReason DisabledByPolicy { get; } = new("disabled-by-policy"); + /// The plugin depends on Canvas for its intended functionality. + public static CatalogAgentPluginCompatibilityTag CanvasOnly { get; } = new("canvas-only"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogUnavailableReason left, CatalogUnavailableReason right) => left.Equals(right); + /// The plugin targets GitHub Copilot. + public static CatalogAgentPluginCompatibilityTag GitHubCopilot { get; } = new("github-copilot"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogUnavailableReason left, CatalogUnavailableReason right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogAgentPluginCompatibilityTag left, CatalogAgentPluginCompatibilityTag right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogAgentPluginCompatibilityTag left, CatalogAgentPluginCompatibilityTag right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogUnavailableReason other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogAgentPluginCompatibilityTag other && Equals(other); /// - public bool Equals(CatalogUnavailableReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogAgentPluginCompatibilityTag other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28383,68 +31734,59 @@ public CatalogUnavailableReason(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 CatalogUnavailableReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogAgentPluginCompatibilityTag Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogUnavailableReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogAgentPluginCompatibilityTag value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogUnavailableReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAgentPluginCompatibilityTag)); } } } -/// Authority-computed exposure eligibility, kept separate from tier. The current tier-only Agent Finder response maps to `unknown`, never to a locally inferred eligibility. +/// Canonical Agent Plugin media type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogTrustEligibility : IEquatable +public readonly struct CatalogAgentPluginMediaType : 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 CatalogTrustEligibility(string value) + public CatalogAgentPluginMediaType(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; - /// Eligible for default catalogue exposure. - public static CatalogTrustEligibility Default { get; } = new("default"); - - /// Eligible only when expanded or community results are requested. - public static CatalogTrustEligibility Expanded { get; } = new("expanded"); - - /// Not eligible for normal catalogue exposure. - public static CatalogTrustEligibility Hidden { get; } = new("hidden"); - - /// The authority did not supply an eligibility decision. - public static CatalogTrustEligibility Unknown { get; } = new("unknown"); + /// A GitHub Copilot Agent Plugin descriptor. + public static CatalogAgentPluginMediaType ApplicationVndGitHubCopilotPlugin { get; } = new("application/vnd.github.copilot-plugin"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogTrustEligibility left, CatalogTrustEligibility right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogAgentPluginMediaType left, CatalogAgentPluginMediaType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogTrustEligibility left, CatalogTrustEligibility right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogAgentPluginMediaType left, CatalogAgentPluginMediaType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogTrustEligibility other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogAgentPluginMediaType other && Equals(other); /// - public bool Equals(CatalogTrustEligibility other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogAgentPluginMediaType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28452,59 +31794,59 @@ public CatalogTrustEligibility(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 CatalogTrustEligibility Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogAgentPluginMediaType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogTrustEligibility value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogAgentPluginMediaType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogTrustEligibility)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAgentPluginMediaType)); } } } -/// Bounded authority that supplied a catalogue trust observation. +/// Relationship of the backend-reported count to the complete query result set. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogTrustSource : IEquatable +public readonly struct CatalogSearchTotalCountRelation : 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 CatalogTrustSource(string value) + public CatalogSearchTotalCountRelation(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; - /// GitHub Agent Finder supplied the trust field on its search result. - public static CatalogTrustSource AgentFinder { get; } = new("agent-finder"); + /// No exact/full-query or lower-bound guarantee is available. + public static CatalogSearchTotalCountRelation Unknown { get; } = new("unknown"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogTrustSource left, CatalogTrustSource right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogSearchTotalCountRelation left, CatalogSearchTotalCountRelation right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogTrustSource left, CatalogTrustSource right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogSearchTotalCountRelation left, CatalogSearchTotalCountRelation right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogTrustSource other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogSearchTotalCountRelation other && Equals(other); /// - public bool Equals(CatalogTrustSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogSearchTotalCountRelation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28512,59 +31854,65 @@ public CatalogTrustSource(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 CatalogTrustSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogSearchTotalCountRelation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogTrustSource value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogSearchTotalCountRelation value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogTrustSource)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogSearchTotalCountRelation)); } } } -/// Schema version of the catalogue trust snapshot envelope. +/// What kind of resource a catalog candidate describes. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogTrustSnapshotSchemaVersion : IEquatable +public readonly struct CatalogCandidateKind : 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 CatalogTrustSnapshotSchemaVersion(string value) + public CatalogCandidateKind(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; - /// Initial envelope carrying one bounded service tier or one explicit unavailable state. - public static CatalogTrustSnapshotSchemaVersion V1 { get; } = new("v1"); + /// An MCP server, which can be planned for installation. + public static CatalogCandidateKind McpServer { get; } = new("mcp-server"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogTrustSnapshotSchemaVersion left, CatalogTrustSnapshotSchemaVersion right) => left.Equals(right); + /// An AI skill, which is discoverable but not installable through this surface. + public static CatalogCandidateKind AiSkill { get; } = new("ai-skill"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogTrustSnapshotSchemaVersion left, CatalogTrustSnapshotSchemaVersion right) => !(left == right); + /// An inert Agent Plugin candidate, available only when explicitly requested. + public static CatalogCandidateKind Plugin { get; } = new("plugin"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogCandidateKind left, CatalogCandidateKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogCandidateKind left, CatalogCandidateKind right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogTrustSnapshotSchemaVersion other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogCandidateKind other && Equals(other); /// - public bool Equals(CatalogTrustSnapshotSchemaVersion other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogCandidateKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28572,62 +31920,68 @@ public CatalogTrustSnapshotSchemaVersion(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 CatalogTrustSnapshotSchemaVersion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogCandidateKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogTrustSnapshotSchemaVersion value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogCandidateKind value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogTrustSnapshotSchemaVersion)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogCandidateKind)); } } } -/// Service-computed trust tier currently emitted by Agent Finder. It is independent of search score, popularity, and client-side ranking. +/// Terminal outcome declared for a retained catalog selection group. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogTrustTier : IEquatable +public readonly struct CatalogSelectionDecision : 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 CatalogTrustTier(string value) + public CatalogSelectionDecision(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; - /// Tier one as assigned by the catalogue authority. - public static CatalogTrustTier T1 { get; } = new("T1"); + /// Choose the candidate named by selectionRef. + public static CatalogSelectionDecision Selected { get; } = new("selected"); - /// Tier two as assigned by the catalogue authority. - public static CatalogTrustTier T2 { get; } = new("T2"); + /// Explicitly decline every candidate in the search. + public static CatalogSelectionDecision Declined { get; } = new("declined"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogTrustTier left, CatalogTrustTier right) => left.Equals(right); + /// Cancel the selection interaction without choosing a candidate. + public static CatalogSelectionDecision Cancelled { get; } = new("cancelled"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogTrustTier left, CatalogTrustTier right) => !(left == right); + /// Declare that the host's live interaction deadline elapsed while the reference remained valid. + public static CatalogSelectionDecision TimedOut { get; } = new("timed-out"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CatalogSelectionDecision left, CatalogSelectionDecision right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CatalogSelectionDecision left, CatalogSelectionDecision right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogTrustTier other && Equals(other); + public override bool Equals(object? obj) => obj is CatalogSelectionDecision other && Equals(other); /// - public bool Equals(CatalogTrustTier other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CatalogSelectionDecision other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28635,74 +31989,62 @@ public CatalogTrustTier(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 CatalogTrustTier Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CatalogSelectionDecision Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogTrustTier value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CatalogSelectionDecision value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogTrustTier)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogSelectionDecision)); } } } -/// Configuration ownership and setup observations, distinct from tool permissions. +/// Where completed plugin content was staged before atomic promotion. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpInstallationState : IEquatable +public readonly struct PluginInstallStagingMode : 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 McpInstallationState(string value) + public PluginInstallStagingMode(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; - /// Owned configuration exists; inventory alone does not grant activation. - public static McpInstallationState NeedsSetup { get; } = new("needs-setup"); - - /// The selected authorised session reports an active installation. - public static McpInstallationState Active { get; } = new("active"); - - /// The selected server requires explicit sign-in. - public static McpInstallationState AuthenticationRequired { get; } = new("authentication-required"); - - /// The selected server could not be activated. - public static McpInstallationState ActivationFailed { get; } = new("activation-failed"); - - /// Owned configuration no longer matches its receipt. - public static McpInstallationState ConfigurationModified { get; } = new("configuration-modified"); + /// A sibling of the installed-plugins root, outside the recursively watched tree. + public static PluginInstallStagingMode External { get; } = new("external"); - /// Confirmed durable work or unsafe evidence requires recovery. - public static McpInstallationState RecoveryRequired { get; } = new("recovery-required"); + /// A sibling of the destination plugin directory, used when external staging is unavailable. + public static PluginInstallStagingMode DestinationSibling { get; } = new("destination_sibling"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpInstallationState left, McpInstallationState right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(PluginInstallStagingMode left, PluginInstallStagingMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpInstallationState left, McpInstallationState right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(PluginInstallStagingMode left, PluginInstallStagingMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpInstallationState other && Equals(other); + public override bool Equals(object? obj) => obj is PluginInstallStagingMode other && Equals(other); /// - public bool Equals(McpInstallationState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(PluginInstallStagingMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28710,125 +32052,74 @@ public McpInstallationState(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 McpInstallationState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override PluginInstallStagingMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpInstallationState value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, PluginInstallStagingMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpInstallationState)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(PluginInstallStagingMode)); } } } -/// Bounded refusal categories, without echoing handles, credentials or configuration. +/// Where the agent definition was loaded from. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpInstallationFailureReason : IEquatable +public readonly struct AgentInfoSource : 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 McpInstallationFailureReason(string value) + public AgentInfoSource(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 selected choice or request is unsupported or malformed. - public static McpInstallationFailureReason InvalidRequest { get; } = new("invalid-request"); - - /// The bounded original-connection operation limit was reached. - public static McpInstallationFailureReason OperationLimit { get; } = new("operation-limit"); - - /// The original operation was cancelled. - public static McpInstallationFailureReason Cancelled { get; } = new("cancelled"); - - /// Required installation capabilities were omitted. - public static McpInstallationFailureReason CapabilityRequired { get; } = new("capability-required"); - - /// The original host cannot receive human confirmation. - public static McpInstallationFailureReason ConfirmationUnavailable { get; } = new("confirmation-unavailable"); - - /// The confirmation response is malformed or mismatched. - public static McpInstallationFailureReason ConfirmationInvalid { get; } = new("confirmation-invalid"); - - /// Existing authenticated session and host authority is unavailable. - public static McpInstallationFailureReason PolicyContextUnavailable { get; } = new("policy-context-unavailable"); - - /// The original authority or policy changed. - public static McpInstallationFailureReason PolicyChanged { get; } = new("policy-changed"); - - /// Current managed policy refuses the operation. - public static McpInstallationFailureReason PolicyDenied { get; } = new("policy-denied"); - - /// Configuration changed after the reviewed snapshot. - public static McpInstallationFailureReason ConfigurationChanged { get; } = new("configuration-changed"); - - /// Installed configuration no longer matches ownership evidence. - public static McpInstallationFailureReason ConfigurationModified { get; } = new("configuration-modified"); - - /// No matching owned resource or original operation exists. - public static McpInstallationFailureReason ResourceNotFound { get; } = new("resource-not-found"); - - /// The original plan deadline elapsed. - public static McpInstallationFailureReason PlanExpired { get; } = new("plan-expired"); - - /// The one-use plan or prepared operation was already consumed. - public static McpInstallationFailureReason PlanReplayed { get; } = new("plan-replayed"); - - /// The handle belongs to a different runtime, session or connection. - public static McpInstallationFailureReason ForeignRuntime { get; } = new("foreign-runtime"); - - /// Fresh bound planning is required. - public static McpInstallationFailureReason ReplanRequired { get; } = new("replan-required"); - - /// Exact original source revalidation is unsupported. - public static McpInstallationFailureReason SourceRevalidationUnavailable { get; } = new("source-revalidation-unavailable"); - - /// The source differs from the retained commitment. - public static McpInstallationFailureReason SourceChanged { get; } = new("source-changed"); + /// Agent loaded from the user's personal agent configuration. + public static AgentInfoSource User { get; } = new("user"); - /// The original source could not be retrieved safely. - public static McpInstallationFailureReason SourceUnavailable { get; } = new("source-unavailable"); + /// Agent loaded from the current project's repository configuration. + public static AgentInfoSource Project { get; } = new("project"); - /// Authoritative Registry interpretation is unavailable. - public static McpInstallationFailureReason RegistryUnavailable { get; } = new("registry-unavailable"); + /// Agent inherited from a parent project or workspace. + public static AgentInfoSource Inherited { get; } = new("inherited"); - /// The selected secret backend is unavailable. - public static McpInstallationFailureReason SecretStoreUnavailable { get; } = new("secret-store-unavailable"); + /// Agent provided by a remote runtime or service. + public static AgentInfoSource Remote { get; } = new("remote"); - /// Required owned admission and lifecycle support is absent. - public static McpInstallationFailureReason LifecycleUnavailable { get; } = new("lifecycle-unavailable"); + /// Agent contributed by an installed plugin. + public static AgentInfoSource Plugin { get; } = new("plugin"); - /// A storage operation failed; inspect any allocated operation before retrying. - public static McpInstallationFailureReason WriteFailed { get; } = new("write-failed"); + /// Agent built into the Copilot runtime. + public static AgentInfoSource Builtin { get; } = new("builtin"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpInstallationFailureReason left, McpInstallationFailureReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentInfoSource left, AgentInfoSource right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpInstallationFailureReason left, McpInstallationFailureReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentInfoSource left, AgentInfoSource right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpInstallationFailureReason other && Equals(other); + public override bool Equals(object? obj) => obj is AgentInfoSource other && Equals(other); /// - public bool Equals(McpInstallationFailureReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentInfoSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28836,62 +32127,62 @@ public McpInstallationFailureReason(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 McpInstallationFailureReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentInfoSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpInstallationFailureReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, AgentInfoSource value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpInstallationFailureReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentInfoSource)); } } } -/// Explicit backend selection is part of the final review; failures never switch backends. +/// Which tier this directory belongs to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpInstallationSecretStorage : IEquatable +public readonly struct AgentDiscoveryPathScope : 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 McpInstallationSecretStorage(string value) + public AgentDiscoveryPathScope(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 selected operating-system keychain, without fallback to file storage. - public static McpInstallationSecretStorage Keychain { get; } = new("keychain"); + /// The user's personal agent configuration directory. + public static AgentDiscoveryPathScope User { get; } = new("user"); - /// The explicitly selected private file backend. - public static McpInstallationSecretStorage PrivateFile { get; } = new("private-file"); + /// A project's repository agent directory. + public static AgentDiscoveryPathScope Project { get; } = new("project"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpInstallationSecretStorage left, McpInstallationSecretStorage right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentDiscoveryPathScope left, AgentDiscoveryPathScope right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpInstallationSecretStorage left, McpInstallationSecretStorage right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentDiscoveryPathScope left, AgentDiscoveryPathScope right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpInstallationSecretStorage other && Equals(other); + public override bool Equals(object? obj) => obj is AgentDiscoveryPathScope other && Equals(other); /// - public bool Equals(McpInstallationSecretStorage other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentDiscoveryPathScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28899,62 +32190,68 @@ public McpInstallationSecretStorage(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 McpInstallationSecretStorage Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentDiscoveryPathScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpInstallationSecretStorage value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, AgentDiscoveryPathScope value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpInstallationSecretStorage)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentDiscoveryPathScope)); } } } -/// Persisted extension discovery source. +/// Where this source lives — used for UI grouping. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct DiscoveredExtensionSource : IEquatable +public readonly struct InstructionSourceLocation : 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 DiscoveredExtensionSource(string value) + public InstructionSourceLocation(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; - /// Extension discovered from the user's extensions directory. - public static DiscoveredExtensionSource User { get; } = new("user"); + /// Instructions live in user-level configuration. + public static InstructionSourceLocation User { get; } = new("user"); - /// Extension contributed by an installed plugin. - public static DiscoveredExtensionSource Plugin { get; } = new("plugin"); + /// Instructions live in repository-level configuration. + public static InstructionSourceLocation Repository { get; } = new("repository"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(DiscoveredExtensionSource left, DiscoveredExtensionSource right) => left.Equals(right); + /// Instructions live under the current working directory. + public static InstructionSourceLocation WorkingDirectory { get; } = new("working-directory"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(DiscoveredExtensionSource left, DiscoveredExtensionSource right) => !(left == right); + /// 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 DiscoveredExtensionSource other && Equals(other); + public override bool Equals(object? obj) => obj is InstructionSourceLocation other && Equals(other); /// - public bool Equals(DiscoveredExtensionSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(InstructionSourceLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -28962,65 +32259,77 @@ public DiscoveredExtensionSource(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 DiscoveredExtensionSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override InstructionSourceLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, DiscoveredExtensionSource value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, InstructionSourceLocation value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiscoveredExtensionSource)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceLocation)); } } } -/// Effective extension loading and agent-management mode. +/// Category of instruction source — used for merge logic. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct DiscoveredExtensionMode : IEquatable +public readonly struct InstructionSourceType : 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 DiscoveredExtensionMode(string value) + public InstructionSourceType(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; - /// Extensions are not loaded. - public static DiscoveredExtensionMode Disabled { get; } = new("disabled"); + /// Instructions loaded from the user's home configuration. + public static InstructionSourceType Home { get; } = new("home"); - /// Extensions are loaded, but the agent cannot create, reload, or manage them. - public static DiscoveredExtensionMode LoadOnly { get; } = new("load_only"); + /// Instructions loaded from repository-scoped files. + public static InstructionSourceType Repo { get; } = new("repo"); - /// Extensions are loaded and the agent can create, reload, and manage them. - public static DiscoveredExtensionMode LoadAndAugment { get; } = new("load_and_augment"); + /// Instructions loaded from model-specific files. + public static InstructionSourceType Model { get; } = new("model"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(DiscoveredExtensionMode left, DiscoveredExtensionMode right) => left.Equals(right); + /// Instructions loaded from VS Code instruction files. + public static InstructionSourceType Vscode { get; } = new("vscode"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(DiscoveredExtensionMode left, DiscoveredExtensionMode right) => !(left == right); + /// 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 DiscoveredExtensionMode other && Equals(other); + public override bool Equals(object? obj) => obj is InstructionSourceType other && Equals(other); /// - public bool Equals(DiscoveredExtensionMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(InstructionSourceType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29028,59 +32337,62 @@ public DiscoveredExtensionMode(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 DiscoveredExtensionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override InstructionSourceType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, DiscoveredExtensionMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, InstructionSourceType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiscoveredExtensionMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceType)); } } } -/// Defines the allowed values. +/// 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 SkillInstallationScope : IEquatable +public readonly struct InstructionDiscoveryPathKind : 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 SkillInstallationScope(string value) + public InstructionDiscoveryPathKind(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 user's personal Copilot home. - public static SkillInstallationScope Personal { get; } = new("personal"); + /// The target is a single instruction file. + public static InstructionDiscoveryPathKind File { get; } = new("file"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SkillInstallationScope left, SkillInstallationScope right) => left.Equals(right); + /// The target is a directory that holds instruction files. + public static InstructionDiscoveryPathKind Directory { get; } = new("directory"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SkillInstallationScope left, SkillInstallationScope right) => !(left == right); + /// 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 SkillInstallationScope other && Equals(other); + public override bool Equals(object? obj) => obj is InstructionDiscoveryPathKind other && Equals(other); /// - public bool Equals(SkillInstallationScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(InstructionDiscoveryPathKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29088,65 +32400,68 @@ public SkillInstallationScope(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 SkillInstallationScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override InstructionDiscoveryPathKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SkillInstallationScope value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, InstructionDiscoveryPathKind value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillInstallationScope)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionDiscoveryPathKind)); } } } -/// Owned Skill state observed from files and receipts. +/// Which tier this target belongs to. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SkillInstallationOwnershipState : IEquatable +public readonly struct InstructionDiscoveryPathLocation : 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 SkillInstallationOwnershipState(string value) + public InstructionDiscoveryPathLocation(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; - /// Owned files and receipt evidence match. - public static SkillInstallationOwnershipState Intact { get; } = new("intact"); + /// Instructions live in user-level configuration. + public static InstructionDiscoveryPathLocation User { get; } = new("user"); - /// Owned files no longer match the receipt. - public static SkillInstallationOwnershipState Modified { get; } = new("modified"); + /// Instructions live in repository-level configuration. + public static InstructionDiscoveryPathLocation Repository { get; } = new("repository"); - /// Ownership evidence requires recovery before mutation. - public static SkillInstallationOwnershipState RecoveryRequired { get; } = new("recovery-required"); + /// Instructions live under the current working directory. + public static InstructionDiscoveryPathLocation WorkingDirectory { get; } = new("working-directory"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SkillInstallationOwnershipState left, SkillInstallationOwnershipState right) => left.Equals(right); + /// Instructions live in plugin-provided configuration. + public static InstructionDiscoveryPathLocation Plugin { get; } = new("plugin"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SkillInstallationOwnershipState left, SkillInstallationOwnershipState right) => !(left == right); + /// 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 SkillInstallationOwnershipState other && Equals(other); + public override bool Equals(object? obj) => obj is InstructionDiscoveryPathLocation other && Equals(other); /// - public bool Equals(SkillInstallationOwnershipState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(InstructionDiscoveryPathLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29154,68 +32469,59 @@ public SkillInstallationOwnershipState(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 SkillInstallationOwnershipState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override InstructionDiscoveryPathLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SkillInstallationOwnershipState value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, InstructionDiscoveryPathLocation value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillInstallationOwnershipState)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionDiscoveryPathLocation)); } } } -/// Bound-session observation after reconciling persisted enablement. +/// Optional completion hint for the input (e.g. 'directory' for filesystem path completion). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SkillInstallationSessionState : IEquatable +public readonly struct SlashCommandInputCompletion : 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 SkillInstallationSessionState(string value) + public SlashCommandInputCompletion(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 selected session has loaded this Skill and it is enabled. - public static SkillInstallationSessionState LoadedEnabled { get; } = new("loaded-enabled"); - - /// The selected session has loaded this Skill or settings and it is disabled. - public static SkillInstallationSessionState LoadedDisabled { get; } = new("loaded-disabled"); - - /// The selected session has not loaded Skills after the latest change. - public static SkillInstallationSessionState NotLoaded { get; } = new("not-loaded"); - - /// The selected session could not be inspected. - public static SkillInstallationSessionState Unknown { get; } = new("unknown"); + /// Input should complete filesystem directories. + public static SlashCommandInputCompletion Directory { get; } = new("directory"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SkillInstallationSessionState left, SkillInstallationSessionState right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SlashCommandInputCompletion left, SlashCommandInputCompletion right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SkillInstallationSessionState left, SkillInstallationSessionState right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SlashCommandInputCompletion left, SlashCommandInputCompletion right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SkillInstallationSessionState other && Equals(other); + public override bool Equals(object? obj) => obj is SlashCommandInputCompletion other && Equals(other); /// - public bool Equals(SkillInstallationSessionState other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SlashCommandInputCompletion other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29223,146 +32529,65 @@ public SkillInstallationSessionState(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 SkillInstallationSessionState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SlashCommandInputCompletion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SkillInstallationSessionState value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SlashCommandInputCompletion value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillInstallationSessionState)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SlashCommandInputCompletion)); } } } -/// Bounded refusal categories for verified Skill installation management. +/// Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SkillInstallationFailureReason : IEquatable +public readonly struct SlashCommandKind : 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 SkillInstallationFailureReason(string value) + public SlashCommandKind(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 selected session does not have the Agent Finder Skill installation feature flag enabled for acquisition. - public static SkillInstallationFailureReason FeatureDisabled { get; } = new("feature-disabled"); - - /// The request is unsupported or malformed. - public static SkillInstallationFailureReason InvalidRequest { get; } = new("invalid-request"); - - /// The bounded operation limit was reached. - public static SkillInstallationFailureReason OperationLimit { get; } = new("operation-limit"); - - /// The original operation was cancelled. - public static SkillInstallationFailureReason Cancelled { get; } = new("cancelled"); - - /// The original host cannot receive human confirmation. - public static SkillInstallationFailureReason ConfirmationUnavailable { get; } = new("confirmation-unavailable"); - - /// The confirmation response is malformed or mismatched. - public static SkillInstallationFailureReason ConfirmationInvalid { get; } = new("confirmation-invalid"); - - /// Existing authenticated session and host authority is unavailable. - public static SkillInstallationFailureReason PolicyContextUnavailable { get; } = new("policy-context-unavailable"); - - /// The original authority or policy changed. - public static SkillInstallationFailureReason PolicyChanged { get; } = new("policy-changed"); - - /// No matching owned resource or original operation exists. - public static SkillInstallationFailureReason ResourceNotFound { get; } = new("resource-not-found"); - - /// The original plan deadline elapsed. - public static SkillInstallationFailureReason PlanExpired { get; } = new("plan-expired"); - - /// The one-use plan was already consumed. - public static SkillInstallationFailureReason PlanReplayed { get; } = new("plan-replayed"); - - /// The handle belongs to a different runtime, session or connection. - public static SkillInstallationFailureReason ForeignRuntime { get; } = new("foreign-runtime"); - - /// The handle is not the expected Skill handle kind. - public static SkillInstallationFailureReason WrongKind { get; } = new("wrong-kind"); - - /// The handle was minted for a different search result or authority. - public static SkillInstallationFailureReason SearchMismatch { get; } = new("search-mismatch"); - - /// The handle or operation expired. - public static SkillInstallationFailureReason Expired { get; } = new("expired"); - - /// The candidate handle is not a verified installable Skill candidate. - public static SkillInstallationFailureReason InvalidCandidate { get; } = new("invalid-candidate"); - - /// The verified Skill descriptor could not be retrieved safely. - public static SkillInstallationFailureReason DescriptorUnavailable { get; } = new("descriptor-unavailable"); - - /// The verified Skill descriptor failed validation. - public static SkillInstallationFailureReason DescriptorInvalid { get; } = new("descriptor-invalid"); - - /// The Skill entrypoint could not be retrieved or verified. - public static SkillInstallationFailureReason EntrypointUnavailable { get; } = new("entrypoint-unavailable"); - - /// The Skill entrypoint is not a valid Skill. - public static SkillInstallationFailureReason InvalidSkill { get; } = new("invalid-skill"); - - /// The reviewed Skill payload could not be acquired. - public static SkillInstallationFailureReason PayloadUnavailable { get; } = new("payload-unavailable"); - - /// The acquired payload no longer matches the reviewed descriptor. - public static SkillInstallationFailureReason PayloadMismatch { get; } = new("payload-mismatch"); - - /// The retained Skill source changed after planning. - public static SkillInstallationFailureReason SourceChanged { get; } = new("source-changed"); - - /// The selected runtime cannot inspect or control the requested lifecycle operation. - public static SkillInstallationFailureReason LifecycleUnavailable { get; } = new("lifecycle-unavailable"); - - /// Durable Skill installation evidence requires recovery before mutation. - public static SkillInstallationFailureReason RecoveryRequired { get; } = new("recovery-required"); - - /// The Skill entrypoint exceeds the bounded complete review size. - public static SkillInstallationFailureReason ReviewTooLarge { get; } = new("review-too-large"); - - /// Skill installation storage or admission is busy. - public static SkillInstallationFailureReason Busy { get; } = new("busy"); - - /// An owned Skill with the same identity or target already exists. - public static SkillInstallationFailureReason AlreadyInstalled { get; } = new("already-installed"); + /// Command implemented by the runtime. + public static SlashCommandKind Builtin { get; } = new("builtin"); - /// Installed Skill files no longer match ownership evidence. - public static SkillInstallationFailureReason ConfigurationModified { get; } = new("configuration-modified"); + /// Command backed by a skill. + public static SlashCommandKind Skill { get; } = new("skill"); - /// A storage operation failed; inspect durable state before retrying. - public static SkillInstallationFailureReason WriteFailed { get; } = new("write-failed"); + /// Command registered by an SDK client or extension. + public static SlashCommandKind Client { get; } = new("client"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SkillInstallationFailureReason left, SkillInstallationFailureReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SlashCommandKind left, SlashCommandKind right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SkillInstallationFailureReason left, SkillInstallationFailureReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SlashCommandKind left, SlashCommandKind right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SkillInstallationFailureReason other && Equals(other); + public override bool Equals(object? obj) => obj is SlashCommandKind other && Equals(other); /// - public bool Equals(SkillInstallationFailureReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SlashCommandKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29370,68 +32595,62 @@ public SkillInstallationFailureReason(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 SkillInstallationFailureReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SlashCommandKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SkillInstallationFailureReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SlashCommandKind value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillInstallationFailureReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SlashCommandKind)); } } } -/// Which tier this directory belongs to. +/// Hosting platform type of the repository. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SkillDiscoveryScope : IEquatable +public readonly struct SessionWorkingDirectoryContextHostType : 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 SkillDiscoveryScope(string value) + public SessionWorkingDirectoryContextHostType(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 project's repository skill directory. - public static SkillDiscoveryScope Project { get; } = new("project"); - - /// The user's personal Copilot skill directory. - public static SkillDiscoveryScope PersonalCopilot { get; } = new("personal-copilot"); - - /// The user's personal agents skill directory. - public static SkillDiscoveryScope PersonalAgents { get; } = new("personal-agents"); + /// The working directory repository is hosted on GitHub. + public static SessionWorkingDirectoryContextHostType GitHub { get; } = new("github"); - /// A configured custom skill directory. - public static SkillDiscoveryScope Custom { get; } = new("custom"); + /// The working directory repository is hosted on Azure DevOps. + public static SessionWorkingDirectoryContextHostType Ado { get; } = new("ado"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SkillDiscoveryScope left, SkillDiscoveryScope right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionWorkingDirectoryContextHostType left, SessionWorkingDirectoryContextHostType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SkillDiscoveryScope left, SkillDiscoveryScope right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionWorkingDirectoryContextHostType left, SessionWorkingDirectoryContextHostType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SkillDiscoveryScope other && Equals(other); + public override bool Equals(object? obj) => obj is SessionWorkingDirectoryContextHostType other && Equals(other); /// - public bool Equals(SkillDiscoveryScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionWorkingDirectoryContextHostType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29439,62 +32658,62 @@ public SkillDiscoveryScope(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 SkillDiscoveryScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionWorkingDirectoryContextHostType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SkillDiscoveryScope value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionWorkingDirectoryContextHostType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SkillDiscoveryScope)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionWorkingDirectoryContextHostType)); } } } -/// Whether an MCP server candidate can be planned for installation. +/// Severity of a managed-settings validation finding. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogMcpServerInstallability : IEquatable +public readonly struct ManagedSettingsDiagnosticSeverity : 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 CatalogMcpServerInstallability(string value) + public ManagedSettingsDiagnosticSeverity(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// An install plan can be computed for this MCP server candidate. - public static CatalogMcpServerInstallability Installable { get; } = new("installable"); + /// The runtime rejects the document. + public static ManagedSettingsDiagnosticSeverity Error { get; } = new("error"); - /// Policy forbids installing this MCP server candidate. - public static CatalogMcpServerInstallability NotInstallablePolicy { get; } = new("not-installable-policy"); + /// 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 ==(CatalogMcpServerInstallability left, CatalogMcpServerInstallability right) => left.Equals(right); + /// 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 !=(CatalogMcpServerInstallability left, CatalogMcpServerInstallability right) => !(left == 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 CatalogMcpServerInstallability other && Equals(other); + public override bool Equals(object? obj) => obj is ManagedSettingsDiagnosticSeverity other && Equals(other); /// - public bool Equals(CatalogMcpServerInstallability other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ManagedSettingsDiagnosticSeverity other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29502,71 +32721,65 @@ public CatalogMcpServerInstallability(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 CatalogMcpServerInstallability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ManagedSettingsDiagnosticSeverity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogMcpServerInstallability value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ManagedSettingsDiagnosticSeverity value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogMcpServerInstallability)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsDiagnosticSeverity)); } } } -/// Typed installability state for an AI skill candidate. +/// A channel accepted by managedSettings.compose. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogAiSkillInstallability : IEquatable +public readonly struct ManagedSettingsChannel : 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 CatalogAiSkillInstallability(string value) + public ManagedSettingsChannel(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 AI skill candidate carries verified materialisation metadata and the selected session may plan installation. - public static CatalogAiSkillInstallability Installable { get; } = new("installable"); - - /// Skill installation is understood but disabled for the selected session. - public static CatalogAiSkillInstallability FeatureDisabled { get; } = new("feature-disabled"); - - /// The candidate lacks verified materialisation metadata required for installation. - public static CatalogAiSkillInstallability MaterialisationUnavailable { get; } = new("materialisation-unavailable"); + /// Device policy, the strongest channel. + public static ManagedSettingsChannel Device { get; } = new("device"); - /// Policy refuses Skill installation for the selected session or authority. - public static CatalogAiSkillInstallability PolicyForbids { get; } = new("policy-forbids"); + /// Account or organization policy. + public static ManagedSettingsChannel Server { get; } = new("server"); - /// Compatibility value for discovery-only callers that did not negotiate Skill installation. - public static CatalogAiSkillInstallability NotInstallableKind { get; } = new("not-installable-kind"); + /// 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 ==(CatalogAiSkillInstallability left, CatalogAiSkillInstallability right) => left.Equals(right); + /// 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 !=(CatalogAiSkillInstallability left, CatalogAiSkillInstallability right) => !(left == 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 CatalogAiSkillInstallability other && Equals(other); + public override bool Equals(object? obj) => obj is ManagedSettingsChannel other && Equals(other); /// - public bool Equals(CatalogAiSkillInstallability other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ManagedSettingsChannel other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29574,59 +32787,62 @@ public CatalogAiSkillInstallability(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 CatalogAiSkillInstallability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ManagedSettingsChannel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogAiSkillInstallability value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ManagedSettingsChannel value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAiSkillInstallability)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsChannel)); } } } -/// Canonical AI skill media type. +/// Path conventions used by this filesystem. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogAiSkillMediaType : IEquatable +public readonly struct SessionFsSetProviderConventions : 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 CatalogAiSkillMediaType(string value) + public SessionFsSetProviderConventions(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// An AI skill card. - public static CatalogAiSkillMediaType ApplicationAiSkill { get; } = new("application/ai-skill"); + /// Paths use Windows path conventions. + public static SessionFsSetProviderConventions Windows { get; } = new("windows"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogAiSkillMediaType left, CatalogAiSkillMediaType right) => left.Equals(right); + /// Paths use POSIX path conventions. + public static SessionFsSetProviderConventions Posix { get; } = new("posix"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogAiSkillMediaType left, CatalogAiSkillMediaType right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionFsSetProviderConventions left, SessionFsSetProviderConventions right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionFsSetProviderConventions left, SessionFsSetProviderConventions right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogAiSkillMediaType other && Equals(other); + public override bool Equals(object? obj) => obj is SessionFsSetProviderConventions other && Equals(other); /// - public bool Equals(CatalogAiSkillMediaType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionFsSetProviderConventions other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29634,65 +32850,62 @@ public CatalogAiSkillMediaType(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 CatalogAiSkillMediaType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionFsSetProviderConventions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogAiSkillMediaType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionFsSetProviderConventions value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAiSkillMediaType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsSetProviderConventions)); } } } -/// Explicit Agent Plugin compatibility declared by exact catalog tags. Clients must not infer these values from display text or other metadata. +/// Repository host type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogAgentPluginCompatibilityTag : IEquatable +public readonly struct SessionContextHostType : 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 CatalogAgentPluginCompatibilityTag(string value) + public SessionContextHostType(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 plugin contributes at least one GitHub Copilot Canvas. - public static CatalogAgentPluginCompatibilityTag Canvas { get; } = new("canvas"); - - /// The plugin depends on Canvas for its intended functionality. - public static CatalogAgentPluginCompatibilityTag CanvasOnly { get; } = new("canvas-only"); + /// Session repository is hosted on GitHub. + public static SessionContextHostType GitHub { get; } = new("github"); - /// The plugin targets GitHub Copilot. - public static CatalogAgentPluginCompatibilityTag GitHubCopilot { get; } = new("github-copilot"); + /// Session repository is hosted on Azure DevOps. + public static SessionContextHostType Ado { get; } = new("ado"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogAgentPluginCompatibilityTag left, CatalogAgentPluginCompatibilityTag right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionContextHostType left, SessionContextHostType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogAgentPluginCompatibilityTag left, CatalogAgentPluginCompatibilityTag right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionContextHostType left, SessionContextHostType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogAgentPluginCompatibilityTag other && Equals(other); + public override bool Equals(object? obj) => obj is SessionContextHostType other && Equals(other); /// - public bool Equals(CatalogAgentPluginCompatibilityTag other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionContextHostType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29700,59 +32913,68 @@ public CatalogAgentPluginCompatibilityTag(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 CatalogAgentPluginCompatibilityTag Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionContextHostType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogAgentPluginCompatibilityTag value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionContextHostType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAgentPluginCompatibilityTag)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionContextHostType)); } } } -/// Canonical Agent Plugin media type. +/// What a remote host says one of its sessions is doing right now. Deliberately coarse: this is what a host can report for EVERY session in a catalogue listing, without a client subscribing to each one. AHP's `SessionSummary.status` is the source today; `input-needed` covers both a permission prompt and an `ask_user` question, since the summary does not say which. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogAgentPluginMediaType : IEquatable +public readonly struct RemoteSessionHostStatus : 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 CatalogAgentPluginMediaType(string value) + public RemoteSessionHostStatus(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 GitHub Copilot Agent Plugin descriptor. - public static CatalogAgentPluginMediaType ApplicationVndGitHubCopilotPlugin { get; } = new("application/vnd.github.copilot-plugin"); + /// No turn is running. + public static RemoteSessionHostStatus Idle { get; } = new("idle"); + + /// A turn is running. + public static RemoteSessionHostStatus Working { get; } = new("working"); + + /// The session is blocked on the user: a permission prompt or an `ask_user` question. + public static RemoteSessionHostStatus InputNeeded { get; } = new("input-needed"); + + /// The session ended its last turn in an error. + public static RemoteSessionHostStatus Error { get; } = new("error"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogAgentPluginMediaType left, CatalogAgentPluginMediaType right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(RemoteSessionHostStatus left, RemoteSessionHostStatus right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogAgentPluginMediaType left, CatalogAgentPluginMediaType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(RemoteSessionHostStatus left, RemoteSessionHostStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogAgentPluginMediaType other && Equals(other); + public override bool Equals(object? obj) => obj is RemoteSessionHostStatus other && Equals(other); /// - public bool Equals(CatalogAgentPluginMediaType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(RemoteSessionHostStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29760,59 +32982,62 @@ public CatalogAgentPluginMediaType(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 CatalogAgentPluginMediaType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override RemoteSessionHostStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogAgentPluginMediaType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, RemoteSessionHostStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogAgentPluginMediaType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(RemoteSessionHostStatus)); } } } -/// Relationship of the backend-reported count to the complete query result set. +/// Whether the remote task originated from CCA or CLI `--remote`. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogSearchTotalCountRelation : IEquatable +public readonly struct RemoteSessionMetadataTaskType : 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 CatalogSearchTotalCountRelation(string value) + public RemoteSessionMetadataTaskType(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; - /// No exact/full-query or lower-bound guarantee is available. - public static CatalogSearchTotalCountRelation Unknown { get; } = new("unknown"); + /// GitHub Copilot coding agent task. + public static RemoteSessionMetadataTaskType Cca { get; } = new("cca"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogSearchTotalCountRelation left, CatalogSearchTotalCountRelation right) => left.Equals(right); + /// CLI remote task. + public static RemoteSessionMetadataTaskType Cli { get; } = new("cli"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogSearchTotalCountRelation left, CatalogSearchTotalCountRelation right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(RemoteSessionMetadataTaskType left, RemoteSessionMetadataTaskType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(RemoteSessionMetadataTaskType left, RemoteSessionMetadataTaskType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogSearchTotalCountRelation other && Equals(other); + public override bool Equals(object? obj) => obj is RemoteSessionMetadataTaskType other && Equals(other); /// - public bool Equals(CatalogSearchTotalCountRelation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(RemoteSessionMetadataTaskType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29820,65 +33045,62 @@ public CatalogSearchTotalCountRelation(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 CatalogSearchTotalCountRelation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override RemoteSessionMetadataTaskType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogSearchTotalCountRelation value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, RemoteSessionMetadataTaskType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogSearchTotalCountRelation)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(RemoteSessionMetadataTaskType)); } } } -/// What kind of resource a catalog candidate describes. +/// Step status. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogCandidateKind : IEquatable +public readonly struct SessionsOpenProgressStatus : 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 CatalogCandidateKind(string value) + public SessionsOpenProgressStatus(string value) { ArgumentException.ThrowIfNullOrWhiteSpace(value); _value = value; } - /// Gets the value associated with this . + /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// An MCP server, which can be planned for installation. - public static CatalogCandidateKind McpServer { get; } = new("mcp-server"); - - /// An AI skill, which is discoverable but not installable through this surface. - public static CatalogCandidateKind AiSkill { get; } = new("ai-skill"); + /// The step has started and has not yet finished. + public static SessionsOpenProgressStatus InProgress { get; } = new("in-progress"); - /// An inert Agent Plugin candidate, available only when explicitly requested. - public static CatalogCandidateKind Plugin { get; } = new("plugin"); + /// The step has completed successfully. + public static SessionsOpenProgressStatus Complete { get; } = new("complete"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogCandidateKind left, CatalogCandidateKind right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionsOpenProgressStatus left, SessionsOpenProgressStatus right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogCandidateKind left, CatalogCandidateKind right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionsOpenProgressStatus left, SessionsOpenProgressStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogCandidateKind other && Equals(other); + public override bool Equals(object? obj) => obj is SessionsOpenProgressStatus other && Equals(other); /// - public bool Equals(CatalogCandidateKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionsOpenProgressStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29886,68 +33108,74 @@ public CatalogCandidateKind(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 CatalogCandidateKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionsOpenProgressStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogCandidateKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionsOpenProgressStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogCandidateKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionsOpenProgressStatus)); } } } -/// Terminal outcome declared for a retained catalog selection group. +/// Handoff step. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct CatalogSelectionDecision : IEquatable +public readonly struct SessionsOpenProgressStep : 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 CatalogSelectionDecision(string value) + public SessionsOpenProgressStep(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; - /// Choose the candidate named by selectionRef. - public static CatalogSelectionDecision Selected { get; } = new("selected"); + /// Loading the source session's events from the remote service. + public static SessionsOpenProgressStep LoadSession { get; } = new("load-session"); - /// Explicitly decline every candidate in the search. - public static CatalogSelectionDecision Declined { get; } = new("declined"); + /// Validating that the local repository matches the remote session's repository. + public static SessionsOpenProgressStep ValidateRepo { get; } = new("validate-repo"); - /// Cancel the selection interaction without choosing a candidate. - public static CatalogSelectionDecision Cancelled { get; } = new("cancelled"); + /// Checking the local working tree for uncommitted changes that would block the handoff. + public static SessionsOpenProgressStep CheckChanges { get; } = new("check-changes"); - /// Declare that the host's live interaction deadline elapsed while the reference remained valid. - public static CatalogSelectionDecision TimedOut { get; } = new("timed-out"); + /// Checking out the branch associated with the remote session in the local working tree. + public static SessionsOpenProgressStep CheckoutBranch { get; } = new("checkout-branch"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(CatalogSelectionDecision left, CatalogSelectionDecision right) => left.Equals(right); + /// Creating the new local session and seeding it with the source session's events. + public static SessionsOpenProgressStep CreateSession { get; } = new("create-session"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(CatalogSelectionDecision left, CatalogSelectionDecision right) => !(left == right); + /// Persisting the newly-created local session to disk. + public static SessionsOpenProgressStep SaveSession { get; } = new("save-session"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionsOpenProgressStep left, SessionsOpenProgressStep right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionsOpenProgressStep left, SessionsOpenProgressStep right) => !(left == right); /// - public override bool Equals(object? obj) => obj is CatalogSelectionDecision other && Equals(other); + public override bool Equals(object? obj) => obj is SessionsOpenProgressStep other && Equals(other); /// - public bool Equals(CatalogSelectionDecision other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionsOpenProgressStep other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -29955,62 +33183,71 @@ public CatalogSelectionDecision(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 CatalogSelectionDecision Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionsOpenProgressStep Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, CatalogSelectionDecision value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionsOpenProgressStep value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CatalogSelectionDecision)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionsOpenProgressStep)); } } } -/// Where completed plugin content was staged before atomic promotion. +/// Outcome of the open request. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct PluginInstallStagingMode : IEquatable +public readonly struct SessionsOpenStatus : 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 PluginInstallStagingMode(string value) + public SessionsOpenStatus(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 sibling of the installed-plugins root, outside the recursively watched tree. - public static PluginInstallStagingMode External { get; } = new("external"); + /// A new session was created. + public static SessionsOpenStatus Created { get; } = new("created"); - /// A sibling of the destination plugin directory, used when external staging is unavailable. - public static PluginInstallStagingMode DestinationSibling { get; } = new("destination_sibling"); + /// An existing session was loaded or reattached. + public static SessionsOpenStatus Resumed { get; } = new("resumed"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(PluginInstallStagingMode left, PluginInstallStagingMode right) => left.Equals(right); + /// No matching persisted session was found. + public static SessionsOpenStatus NotFound { get; } = new("not_found"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(PluginInstallStagingMode left, PluginInstallStagingMode right) => !(left == right); + /// Connected to an existing remote session. + public static SessionsOpenStatus Connected { get; } = new("connected"); + + /// Remote session was handed off to a new local session. + public static SessionsOpenStatus HandedOff { get; } = new("handed_off"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionsOpenStatus left, SessionsOpenStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionsOpenStatus left, SessionsOpenStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is PluginInstallStagingMode other && Equals(other); + public override bool Equals(object? obj) => obj is SessionsOpenStatus other && Equals(other); /// - public bool Equals(PluginInstallStagingMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionsOpenStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30018,74 +33255,62 @@ public PluginInstallStagingMode(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 PluginInstallStagingMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionsOpenStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, PluginInstallStagingMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionsOpenStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(PluginInstallStagingMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionsOpenStatus)); } } } -/// Where the agent definition was loaded from. +/// Neutral SDK discriminator for the connected remote session kind. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentInfoSource : IEquatable +public readonly struct ConnectedRemoteSessionMetadataKind : 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 AgentInfoSource(string value) + public ConnectedRemoteSessionMetadataKind(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; - /// Agent loaded from the user's personal agent configuration. - public static AgentInfoSource User { get; } = new("user"); - - /// Agent loaded from the current project's repository configuration. - public static AgentInfoSource Project { get; } = new("project"); - - /// Agent inherited from a parent project or workspace. - public static AgentInfoSource Inherited { get; } = new("inherited"); - - /// Agent provided by a remote runtime or service. - public static AgentInfoSource Remote { get; } = new("remote"); - - /// Agent contributed by an installed plugin. - public static AgentInfoSource Plugin { get; } = new("plugin"); + /// Remote CLI session. + public static ConnectedRemoteSessionMetadataKind RemoteSession { get; } = new("remote-session"); - /// Agent built into the Copilot runtime. - public static AgentInfoSource Builtin { get; } = new("builtin"); + /// GitHub Copilot coding agent session. + public static ConnectedRemoteSessionMetadataKind CodingAgent { get; } = new("coding-agent"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentInfoSource left, AgentInfoSource right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ConnectedRemoteSessionMetadataKind left, ConnectedRemoteSessionMetadataKind right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentInfoSource left, AgentInfoSource right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ConnectedRemoteSessionMetadataKind left, ConnectedRemoteSessionMetadataKind right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentInfoSource other && Equals(other); + public override bool Equals(object? obj) => obj is ConnectedRemoteSessionMetadataKind other && Equals(other); /// - public bool Equals(AgentInfoSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ConnectedRemoteSessionMetadataKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30093,62 +33318,65 @@ public AgentInfoSource(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 AgentInfoSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ConnectedRemoteSessionMetadataKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentInfoSource value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ConnectedRemoteSessionMetadataKind value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentInfoSource)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectedRemoteSessionMetadataKind)); } } } -/// Which tier this directory belongs to. +/// Which session sources to include. Defaults to `local` for backward compatibility. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentDiscoveryPathScope : IEquatable +public readonly struct SessionSource : 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 AgentDiscoveryPathScope(string value) + public SessionSource(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 user's personal agent configuration directory. - public static AgentDiscoveryPathScope User { get; } = new("user"); + /// Return only local sessions. + public static SessionSource Local { get; } = new("local"); - /// A project's repository agent directory. - public static AgentDiscoveryPathScope Project { get; } = new("project"); + /// Return only remote sessions. + public static SessionSource Remote { get; } = new("remote"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentDiscoveryPathScope left, AgentDiscoveryPathScope right) => left.Equals(right); + /// Return both local and remote sessions. + public static SessionSource All { get; } = new("all"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentDiscoveryPathScope left, AgentDiscoveryPathScope right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionSource left, SessionSource right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionSource left, SessionSource right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentDiscoveryPathScope other && Equals(other); + public override bool Equals(object? obj) => obj is SessionSource other && Equals(other); /// - public bool Equals(AgentDiscoveryPathScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30156,68 +33384,62 @@ public AgentDiscoveryPathScope(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 AgentDiscoveryPathScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentDiscoveryPathScope value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionSource value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentDiscoveryPathScope)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionSource)); } } } -/// Where this source lives — used for UI grouping. +/// Cursor status: 'ok' means the read succeeded against the requested history; 'expired' means the requested continuation is unavailable. Recovery is endpoint-specific: session.eventLog.read returns a boundary window of remaining active history that may overlap prior pages, while sessions.readPersistedEvents returns an empty terminal page and never switches journal generations. An expired persisted read is not successful completion; a complete persisted snapshot requires cursorStatus 'ok' and hasMore false. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionSourceLocation : IEquatable +public readonly struct EventsCursorStatus : 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 InstructionSourceLocation(string value) + public EventsCursorStatus(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; - /// 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"); + /// The read succeeded against the requested history. + public static EventsCursorStatus Ok { get; } = new("ok"); - /// Instructions live in plugin-provided configuration. - public static InstructionSourceLocation Plugin { get; } = new("plugin"); + /// The requested continuation is unavailable; see the endpoint's recovery semantics. + public static EventsCursorStatus Expired { get; } = new("expired"); - /// 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 equivalent. + public static bool operator ==(EventsCursorStatus left, EventsCursorStatus right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionSourceLocation left, InstructionSourceLocation right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(EventsCursorStatus left, EventsCursorStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is InstructionSourceLocation other && Equals(other); + public override bool Equals(object? obj) => obj is EventsCursorStatus other && Equals(other); /// - public bool Equals(InstructionSourceLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(EventsCursorStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30225,77 +33447,62 @@ public InstructionSourceLocation(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 InstructionSourceLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override EventsCursorStatus 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) + public override void Write(Utf8JsonWriter writer, EventsCursorStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceLocation)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(EventsCursorStatus)); } } } -/// Category of instruction source — used for merge logic. +/// Direction to page through the session's persisted event history. 'forward' pages from the cursor toward newer events; 'backward' returns the newest window first (tail-first) and pages toward older events. Events within a returned batch are always chronological (oldest-to-newest), even for a backward read. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionSourceType : IEquatable +public readonly struct EventsReadDirection : 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 InstructionSourceType(string value) + public EventsReadDirection(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; - /// 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"); + /// Page from the cursor toward newer events (default). + public static EventsReadDirection Forward { get; } = new("forward"); - /// Instructions supplied by an installed plugin. - public static InstructionSourceType Plugin { get; } = new("plugin"); + /// Tail-first: return the newest events and page toward older events. + public static EventsReadDirection Backward { get; } = new("backward"); - /// 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 equivalent. + public static bool operator ==(EventsReadDirection left, EventsReadDirection right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionSourceType left, InstructionSourceType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(EventsReadDirection left, EventsReadDirection right) => !(left == right); /// - public override bool Equals(object? obj) => obj is InstructionSourceType other && Equals(other); + public override bool Equals(object? obj) => obj is EventsReadDirection other && Equals(other); /// - public bool Equals(InstructionSourceType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(EventsReadDirection other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30303,62 +33510,71 @@ public InstructionSourceType(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 InstructionSourceType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override EventsReadDirection 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) + public override void Write(Utf8JsonWriter writer, EventsReadDirection value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionSourceType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(EventsReadDirection)); } } } -/// Whether the target is a single file or a directory of instruction files. +/// Kind of attention required when status === "attention". Meaningful only when status === "attention". [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionDiscoveryPathKind : IEquatable +public readonly struct AgentRegistryLiveTargetEntryAttentionKind : 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 InstructionDiscoveryPathKind(string value) + public AgentRegistryLiveTargetEntryAttentionKind(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 target is a single instruction file. - public static InstructionDiscoveryPathKind File { get; } = new("file"); + /// Session is blocked on an unrecoverable error. + public static AgentRegistryLiveTargetEntryAttentionKind Error { get; } = new("error"); - /// The target is a directory that holds instruction files. - public static InstructionDiscoveryPathKind Directory { get; } = new("directory"); + /// Session is waiting for a tool-permission decision. + public static AgentRegistryLiveTargetEntryAttentionKind Permission { get; } = new("permission"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(InstructionDiscoveryPathKind left, InstructionDiscoveryPathKind right) => left.Equals(right); + /// Session is waiting for the user to approve or reject a plan. + public static AgentRegistryLiveTargetEntryAttentionKind ExitPlan { get; } = new("exit_plan"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionDiscoveryPathKind left, InstructionDiscoveryPathKind right) => !(left == right); + /// Session is waiting on an elicitation prompt. + public static AgentRegistryLiveTargetEntryAttentionKind Elicitation { get; } = new("elicitation"); + + /// Session is waiting for free-form user input. + public static AgentRegistryLiveTargetEntryAttentionKind UserInput { get; } = new("user_input"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentRegistryLiveTargetEntryAttentionKind left, AgentRegistryLiveTargetEntryAttentionKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentRegistryLiveTargetEntryAttentionKind left, AgentRegistryLiveTargetEntryAttentionKind right) => !(left == right); /// - public override bool Equals(object? obj) => obj is InstructionDiscoveryPathKind other && Equals(other); + public override bool Equals(object? obj) => obj is AgentRegistryLiveTargetEntryAttentionKind other && Equals(other); /// - public bool Equals(InstructionDiscoveryPathKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentRegistryLiveTargetEntryAttentionKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30366,68 +33582,62 @@ public InstructionDiscoveryPathKind(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 InstructionDiscoveryPathKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentRegistryLiveTargetEntryAttentionKind 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) + public override void Write(Utf8JsonWriter writer, AgentRegistryLiveTargetEntryAttentionKind value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionDiscoveryPathKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLiveTargetEntryAttentionKind)); } } } -/// Which tier this target belongs to. +/// Process kind tag for the registry entry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct InstructionDiscoveryPathLocation : IEquatable +public readonly struct AgentRegistryLiveTargetEntryKind : 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 InstructionDiscoveryPathLocation(string value) + public AgentRegistryLiveTargetEntryKind(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; - /// 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"); + /// Interactive Copilot CLI exposing a UI server (legacy/normal CLI process). + public static AgentRegistryLiveTargetEntryKind UiServer { get; } = new("ui-server"); - /// Instructions live in plugin-provided configuration. - public static InstructionDiscoveryPathLocation Plugin { get; } = new("plugin"); + /// Headless `--server --managed-server` child spawned by a controller. + public static AgentRegistryLiveTargetEntryKind ManagedServer { get; } = new("managed-server"); - /// 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 equivalent. + public static bool operator ==(AgentRegistryLiveTargetEntryKind left, AgentRegistryLiveTargetEntryKind right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(InstructionDiscoveryPathLocation left, InstructionDiscoveryPathLocation right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentRegistryLiveTargetEntryKind left, AgentRegistryLiveTargetEntryKind right) => !(left == right); /// - public override bool Equals(object? obj) => obj is InstructionDiscoveryPathLocation other && Equals(other); + public override bool Equals(object? obj) => obj is AgentRegistryLiveTargetEntryKind other && Equals(other); /// - public bool Equals(InstructionDiscoveryPathLocation other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentRegistryLiveTargetEntryKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30435,59 +33645,62 @@ public InstructionDiscoveryPathLocation(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 InstructionDiscoveryPathLocation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentRegistryLiveTargetEntryKind 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) + public override void Write(Utf8JsonWriter writer, AgentRegistryLiveTargetEntryKind value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(InstructionDiscoveryPathLocation)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLiveTargetEntryKind)); } } } -/// Optional completion hint for the input (e.g. 'directory' for filesystem path completion). +/// How the most recent turn ended (clean vs aborted). Lets the renderer distinguish done from done_cancelled. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SlashCommandInputCompletion : IEquatable +public readonly struct AgentRegistryLiveTargetEntryLastTerminalEvent : 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 SlashCommandInputCompletion(string value) + public AgentRegistryLiveTargetEntryLastTerminalEvent(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; - /// Input should complete filesystem directories. - public static SlashCommandInputCompletion Directory { get; } = new("directory"); + /// Last turn ended cleanly (model returned a final assistant message). + public static AgentRegistryLiveTargetEntryLastTerminalEvent TurnEnd { get; } = new("turn_end"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SlashCommandInputCompletion left, SlashCommandInputCompletion right) => left.Equals(right); + /// Last turn was aborted (e.g. user interrupted). + public static AgentRegistryLiveTargetEntryLastTerminalEvent Abort { get; } = new("abort"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SlashCommandInputCompletion left, SlashCommandInputCompletion right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentRegistryLiveTargetEntryLastTerminalEvent left, AgentRegistryLiveTargetEntryLastTerminalEvent right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentRegistryLiveTargetEntryLastTerminalEvent left, AgentRegistryLiveTargetEntryLastTerminalEvent right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SlashCommandInputCompletion other && Equals(other); + public override bool Equals(object? obj) => obj is AgentRegistryLiveTargetEntryLastTerminalEvent other && Equals(other); /// - public bool Equals(SlashCommandInputCompletion other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentRegistryLiveTargetEntryLastTerminalEvent other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30495,65 +33708,68 @@ public SlashCommandInputCompletion(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 SlashCommandInputCompletion Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentRegistryLiveTargetEntryLastTerminalEvent Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SlashCommandInputCompletion value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, AgentRegistryLiveTargetEntryLastTerminalEvent value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SlashCommandInputCompletion)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLiveTargetEntryLastTerminalEvent)); } } } -/// Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command. +/// Coarse lifecycle status of the foreground session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SlashCommandKind : IEquatable +public readonly struct AgentRegistryLiveTargetEntryStatus : 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 SlashCommandKind(string value) + public AgentRegistryLiveTargetEntryStatus(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; - /// Command implemented by the runtime. - public static SlashCommandKind Builtin { get; } = new("builtin"); + /// Session is actively processing a turn. + public static AgentRegistryLiveTargetEntryStatus Working { get; } = new("working"); - /// Command backed by a skill. - public static SlashCommandKind Skill { get; } = new("skill"); + /// Session is idle, waiting for input. + public static AgentRegistryLiveTargetEntryStatus Waiting { get; } = new("waiting"); - /// Command registered by an SDK client or extension. - public static SlashCommandKind Client { get; } = new("client"); + /// Last turn completed successfully. + public static AgentRegistryLiveTargetEntryStatus Done { get; } = new("done"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SlashCommandKind left, SlashCommandKind right) => left.Equals(right); + /// Session needs user attention (see attentionKind for the specific reason). + public static AgentRegistryLiveTargetEntryStatus Attention { get; } = new("attention"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SlashCommandKind left, SlashCommandKind right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentRegistryLiveTargetEntryStatus left, AgentRegistryLiveTargetEntryStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentRegistryLiveTargetEntryStatus left, AgentRegistryLiveTargetEntryStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SlashCommandKind other && Equals(other); + public override bool Equals(object? obj) => obj is AgentRegistryLiveTargetEntryStatus other && Equals(other); /// - public bool Equals(SlashCommandKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentRegistryLiveTargetEntryStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30561,62 +33777,65 @@ public SlashCommandKind(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 SlashCommandKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentRegistryLiveTargetEntryStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SlashCommandKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, AgentRegistryLiveTargetEntryStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SlashCommandKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLiveTargetEntryStatus)); } } } -/// Severity of a managed-settings validation finding. +/// Categorized reason no canonical process log could be opened. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ManagedSettingsDiagnosticSeverity : IEquatable +public readonly struct AgentRegistryLogCaptureOpenErrorReason : 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 ManagedSettingsDiagnosticSeverity(string value) + public AgentRegistryLogCaptureOpenErrorReason(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 runtime rejects the document. - public static ManagedSettingsDiagnosticSeverity Error { get; } = new("error"); + /// Filesystem permission denied opening the log file. + public static AgentRegistryLogCaptureOpenErrorReason Permission { get; } = new("permission"); - /// The runtime accepts the document but ignores the flagged content. - public static ManagedSettingsDiagnosticSeverity Warning { get; } = new("warning"); + /// No space left on device. + public static AgentRegistryLogCaptureOpenErrorReason DiskFull { get; } = new("disk_full"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ManagedSettingsDiagnosticSeverity left, ManagedSettingsDiagnosticSeverity right) => left.Equals(right); + /// Other / uncategorized open failure. + public static AgentRegistryLogCaptureOpenErrorReason Other { get; } = new("other"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ManagedSettingsDiagnosticSeverity left, ManagedSettingsDiagnosticSeverity right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentRegistryLogCaptureOpenErrorReason left, AgentRegistryLogCaptureOpenErrorReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentRegistryLogCaptureOpenErrorReason left, AgentRegistryLogCaptureOpenErrorReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ManagedSettingsDiagnosticSeverity other && Equals(other); + public override bool Equals(object? obj) => obj is AgentRegistryLogCaptureOpenErrorReason other && Equals(other); /// - public bool Equals(ManagedSettingsDiagnosticSeverity other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentRegistryLogCaptureOpenErrorReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30624,65 +33843,71 @@ public ManagedSettingsDiagnosticSeverity(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 ManagedSettingsDiagnosticSeverity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentRegistryLogCaptureOpenErrorReason 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) + public override void Write(Utf8JsonWriter writer, AgentRegistryLogCaptureOpenErrorReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsDiagnosticSeverity)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLogCaptureOpenErrorReason)); } } } -/// A channel accepted by managedSettings.compose. +/// Which parameter field was invalid. Omitted when the rejection is not field-specific. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ManagedSettingsChannel : IEquatable +public readonly struct AgentRegistrySpawnValidationErrorField : 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 ManagedSettingsChannel(string value) + public AgentRegistrySpawnValidationErrorField(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; - /// Device policy, the strongest channel. - public static ManagedSettingsChannel Device { get; } = new("device"); + /// The cwd parameter. + public static AgentRegistrySpawnValidationErrorField Cwd { get; } = new("cwd"); - /// Account or organization policy. - public static ManagedSettingsChannel Server { get; } = new("server"); + /// The session name parameter. + public static AgentRegistrySpawnValidationErrorField Name { get; } = new("name"); - /// Session-local helper output, the weakest channel. - public static ManagedSettingsChannel PolicyHelper { get; } = new("policyHelper"); + /// The agentName parameter. + public static AgentRegistrySpawnValidationErrorField AgentName { get; } = new("agentName"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ManagedSettingsChannel left, ManagedSettingsChannel right) => left.Equals(right); + /// The model parameter. + public static AgentRegistrySpawnValidationErrorField Model { get; } = new("model"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ManagedSettingsChannel left, ManagedSettingsChannel right) => !(left == right); + /// The permissionMode parameter. + public static AgentRegistrySpawnValidationErrorField PermissionMode { get; } = new("permissionMode"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentRegistrySpawnValidationErrorField left, AgentRegistrySpawnValidationErrorField right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentRegistrySpawnValidationErrorField left, AgentRegistrySpawnValidationErrorField right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ManagedSettingsChannel other && Equals(other); + public override bool Equals(object? obj) => obj is AgentRegistrySpawnValidationErrorField other && Equals(other); /// - public bool Equals(ManagedSettingsChannel other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentRegistrySpawnValidationErrorField other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30690,62 +33915,74 @@ public ManagedSettingsChannel(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 ManagedSettingsChannel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentRegistrySpawnValidationErrorField 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) + public override void Write(Utf8JsonWriter writer, AgentRegistrySpawnValidationErrorField value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedSettingsChannel)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistrySpawnValidationErrorField)); } } } -/// Path conventions used by this filesystem. +/// Categorized reason for the rejection. Low-cardinality enum so telemetry can aggregate by reason without leaking raw paths or agent/model names. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionFsSetProviderConventions : IEquatable +public readonly struct AgentRegistrySpawnValidationErrorReason : 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 SessionFsSetProviderConventions(string value) + public AgentRegistrySpawnValidationErrorReason(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; - /// Paths use Windows path conventions. - public static SessionFsSetProviderConventions Windows { get; } = new("windows"); + /// Provided cwd does not exist on disk. + public static AgentRegistrySpawnValidationErrorReason CwdNotFound { get; } = new("cwd-not-found"); - /// Paths use POSIX path conventions. - public static SessionFsSetProviderConventions Posix { get; } = new("posix"); + /// Provided cwd exists but is not a directory. + public static AgentRegistrySpawnValidationErrorReason CwdNotDirectory { get; } = new("cwd-not-directory"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionFsSetProviderConventions left, SessionFsSetProviderConventions right) => left.Equals(right); + /// Session name failed validateSessionName. + public static AgentRegistrySpawnValidationErrorReason InvalidName { get; } = new("invalid-name"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionFsSetProviderConventions left, SessionFsSetProviderConventions right) => !(left == right); + /// Requested agent name was not found in builtin or custom agents. + public static AgentRegistrySpawnValidationErrorReason UnknownAgent { get; } = new("unknown-agent"); + + /// Requested model is not available to this session. + public static AgentRegistrySpawnValidationErrorReason UnknownModel { get; } = new("unknown-model"); + + /// Caller asked for permissionMode='yolo' but the controller is not currently in allow-all mode. + public static AgentRegistrySpawnValidationErrorReason YoloNotAllowed { get; } = new("yolo-not-allowed"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentRegistrySpawnValidationErrorReason left, AgentRegistrySpawnValidationErrorReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentRegistrySpawnValidationErrorReason left, AgentRegistrySpawnValidationErrorReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionFsSetProviderConventions other && Equals(other); + public override bool Equals(object? obj) => obj is AgentRegistrySpawnValidationErrorReason other && Equals(other); /// - public bool Equals(SessionFsSetProviderConventions other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentRegistrySpawnValidationErrorReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30753,62 +33990,62 @@ public SessionFsSetProviderConventions(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 SessionFsSetProviderConventions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentRegistrySpawnValidationErrorReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionFsSetProviderConventions value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, AgentRegistrySpawnValidationErrorReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionFsSetProviderConventions)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistrySpawnValidationErrorReason)); } } } -/// Repository host type. +/// Permission posture for the new session. 'yolo' requires the controller-local session to currently be in allow-all mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionContextHostType : IEquatable +public readonly struct AgentRegistrySpawnPermissionMode : 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 SessionContextHostType(string value) + public AgentRegistrySpawnPermissionMode(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; - /// Session repository is hosted on GitHub. - public static SessionContextHostType GitHub { get; } = new("github"); + /// Standard permission posture (prompts for each request). + public static AgentRegistrySpawnPermissionMode Default { get; } = new("default"); - /// Session repository is hosted on Azure DevOps. - public static SessionContextHostType Ado { get; } = new("ado"); + /// Full allow-all (requires the controller-local session to currently be in allow-all mode). + public static AgentRegistrySpawnPermissionMode Yolo { get; } = new("yolo"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionContextHostType left, SessionContextHostType right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AgentRegistrySpawnPermissionMode left, AgentRegistrySpawnPermissionMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionContextHostType left, SessionContextHostType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AgentRegistrySpawnPermissionMode left, AgentRegistrySpawnPermissionMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionContextHostType other && Equals(other); + public override bool Equals(object? obj) => obj is AgentRegistrySpawnPermissionMode other && Equals(other); /// - public bool Equals(SessionContextHostType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AgentRegistrySpawnPermissionMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30816,68 +34053,65 @@ public SessionContextHostType(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 SessionContextHostType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override AgentRegistrySpawnPermissionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionContextHostType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, AgentRegistrySpawnPermissionMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionContextHostType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistrySpawnPermissionMode)); } } } -/// What a remote host says one of its sessions is doing right now. Deliberately coarse: this is what a host can report for EVERY session in a catalogue listing, without a client subscribing to each one. AHP's `SessionSummary.status` is the source today; `input-needed` covers both a permission prompt and an `ask_user` question, since the summary does not say which. +/// Availability. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct RemoteSessionHostStatus : IEquatable +public readonly struct ConnectorDiscoveryAvailability : 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 RemoteSessionHostStatus(string value) + public ConnectorDiscoveryAvailability(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; - /// No turn is running. - public static RemoteSessionHostStatus Idle { get; } = new("idle"); - - /// A turn is running. - public static RemoteSessionHostStatus Working { get; } = new("working"); + /// Enabled. + public static ConnectorDiscoveryAvailability Enabled { get; } = new("enabled"); - /// The session is blocked on the user: a permission prompt or an `ask_user` question. - public static RemoteSessionHostStatus InputNeeded { get; } = new("input-needed"); + /// Disabled. + public static ConnectorDiscoveryAvailability Disabled { get; } = new("disabled"); - /// The session ended its last turn in an error. - public static RemoteSessionHostStatus Error { get; } = new("error"); + /// Unavailable. + public static ConnectorDiscoveryAvailability Unavailable { get; } = new("unavailable"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(RemoteSessionHostStatus left, RemoteSessionHostStatus right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ConnectorDiscoveryAvailability left, ConnectorDiscoveryAvailability right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(RemoteSessionHostStatus left, RemoteSessionHostStatus right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ConnectorDiscoveryAvailability left, ConnectorDiscoveryAvailability right) => !(left == right); /// - public override bool Equals(object? obj) => obj is RemoteSessionHostStatus other && Equals(other); + public override bool Equals(object? obj) => obj is ConnectorDiscoveryAvailability other && Equals(other); /// - public bool Equals(RemoteSessionHostStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ConnectorDiscoveryAvailability other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -30885,62 +34119,83 @@ public RemoteSessionHostStatus(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 RemoteSessionHostStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ConnectorDiscoveryAvailability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, RemoteSessionHostStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ConnectorDiscoveryAvailability value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(RemoteSessionHostStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectorDiscoveryAvailability)); } } } -/// Whether the remote task originated from CCA or CLI `--remote`. +/// Authentication type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct RemoteSessionMetadataTaskType : 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 RemoteSessionMetadataTaskType(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; - /// GitHub Copilot coding agent task. - public static RemoteSessionMetadataTaskType Cca { get; } = new("cca"); + /// Authentication provided by a GitHub App HMAC credential. + public static AuthInfoType Hmac { get; } = new("hmac"); - /// CLI remote task. - public static RemoteSessionMetadataTaskType Cli { get; } = new("cli"); + /// Authentication resolved from environment-provided credentials. + public static AuthInfoType Env { get; } = new("env"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(RemoteSessionMetadataTaskType left, RemoteSessionMetadataTaskType right) => left.Equals(right); + /// Authentication from an interactive user sign-in. + public static AuthInfoType User { get; } = new("user"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(RemoteSessionMetadataTaskType left, RemoteSessionMetadataTaskType right) => !(left == right); + /// 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 RemoteSessionMetadataTaskType other && Equals(other); + public override bool Equals(object? obj) => obj is AuthInfoType other && Equals(other); /// - public bool Equals(RemoteSessionMetadataTaskType 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); @@ -30948,62 +34203,71 @@ public RemoteSessionMetadataTaskType(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 RemoteSessionMetadataTaskType 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, RemoteSessionMetadataTaskType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, AuthInfoType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(RemoteSessionMetadataTaskType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AuthInfoType)); } } } -/// Step status. +/// Authoritative service connection state for one Connector. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionsOpenProgressStatus : IEquatable +public readonly struct ConnectorCatalogStatus : 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 SessionsOpenProgressStatus(string value) + public ConnectorCatalogStatus(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 step has started and has not yet finished. - public static SessionsOpenProgressStatus InProgress { get; } = new("in-progress"); + /// The Connector is available but not connected. + public static ConnectorCatalogStatus NotConnected { get; } = new("not_connected"); - /// The step has completed successfully. - public static SessionsOpenProgressStatus Complete { get; } = new("complete"); + /// The Connector service is still completing connection or consent. + public static ConnectorCatalogStatus Pending { get; } = new("pending"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionsOpenProgressStatus left, SessionsOpenProgressStatus right) => left.Equals(right); + /// The Connector is connected and may contribute MCP servers. + public static ConnectorCatalogStatus Connected { get; } = new("connected"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionsOpenProgressStatus left, SessionsOpenProgressStatus right) => !(left == right); + /// The Connector service reports an unusable connection. + public static ConnectorCatalogStatus Error { get; } = new("error"); + + /// The service returned a future or unrecognized state. + public static ConnectorCatalogStatus Unknown { get; } = new("unknown"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ConnectorCatalogStatus left, ConnectorCatalogStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ConnectorCatalogStatus left, ConnectorCatalogStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionsOpenProgressStatus other && Equals(other); + public override bool Equals(object? obj) => obj is ConnectorCatalogStatus other && Equals(other); /// - public bool Equals(SessionsOpenProgressStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ConnectorCatalogStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31011,74 +34275,68 @@ public SessionsOpenProgressStatus(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 SessionsOpenProgressStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ConnectorCatalogStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionsOpenProgressStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ConnectorCatalogStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionsOpenProgressStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectorCatalogStatus)); } } } -/// Handoff step. +/// The UI mode the agent was in when this message was sent. Defaults to the session's current mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionsOpenProgressStep : IEquatable +public readonly struct SendAgentMode : 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 SessionsOpenProgressStep(string value) + public SendAgentMode(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; - /// Loading the source session's events from the remote service. - public static SessionsOpenProgressStep LoadSession { get; } = new("load-session"); - - /// Validating that the local repository matches the remote session's repository. - public static SessionsOpenProgressStep ValidateRepo { get; } = new("validate-repo"); - - /// Checking the local working tree for uncommitted changes that would block the handoff. - public static SessionsOpenProgressStep CheckChanges { get; } = new("check-changes"); + /// The agent is responding interactively to the user. + public static SendAgentMode Interactive { get; } = new("interactive"); - /// Checking out the branch associated with the remote session in the local working tree. - public static SessionsOpenProgressStep CheckoutBranch { get; } = new("checkout-branch"); + /// The agent is preparing a plan before making changes. + public static SendAgentMode Plan { get; } = new("plan"); - /// Creating the new local session and seeding it with the source session's events. - public static SessionsOpenProgressStep CreateSession { get; } = new("create-session"); + /// The agent is working autonomously toward task completion. + public static SendAgentMode Autopilot { get; } = new("autopilot"); - /// Persisting the newly-created local session to disk. - public static SessionsOpenProgressStep SaveSession { get; } = new("save-session"); + /// The agent is in shell-focused UI mode. + public static SendAgentMode Shell { get; } = new("shell"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionsOpenProgressStep left, SessionsOpenProgressStep right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SendAgentMode left, SendAgentMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionsOpenProgressStep left, SessionsOpenProgressStep right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SendAgentMode left, SendAgentMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionsOpenProgressStep other && Equals(other); + public override bool Equals(object? obj) => obj is SendAgentMode other && Equals(other); /// - public bool Equals(SessionsOpenProgressStep other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SendAgentMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31086,71 +34344,62 @@ public SessionsOpenProgressStep(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 SessionsOpenProgressStep Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SendAgentMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionsOpenProgressStep value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SendAgentMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionsOpenProgressStep)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SendAgentMode)); } } } -/// Outcome of the open request. +/// How to deliver the message. `enqueue` (default) appends to the message queue. `immediate` interjects during an in-progress turn. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionsOpenStatus : IEquatable +public readonly struct SendMode : 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 SessionsOpenStatus(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// A new session was created. - public static SessionsOpenStatus Created { get; } = new("created"); - - /// An existing session was loaded or reattached. - public static SessionsOpenStatus Resumed { get; } = new("resumed"); + public SendMode(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } - /// No matching persisted session was found. - public static SessionsOpenStatus NotFound { get; } = new("not_found"); + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; - /// Connected to an existing remote session. - public static SessionsOpenStatus Connected { get; } = new("connected"); + /// Append the message to the normal session queue. + public static SendMode Enqueue { get; } = new("enqueue"); - /// Remote session was handed off to a new local session. - public static SessionsOpenStatus HandedOff { get; } = new("handed_off"); + /// Interject the message during the in-progress turn. + public static SendMode Immediate { get; } = new("immediate"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionsOpenStatus left, SessionsOpenStatus right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SendMode left, SendMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionsOpenStatus left, SessionsOpenStatus right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SendMode left, SendMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionsOpenStatus other && Equals(other); + public override bool Equals(object? obj) => obj is SendMode other && Equals(other); /// - public bool Equals(SessionsOpenStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SendMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31158,62 +34407,65 @@ public SessionsOpenStatus(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 SessionsOpenStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SendMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionsOpenStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SendMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionsOpenStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SendMode)); } } } -/// Neutral SDK discriminator for the connected remote session kind. +/// Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ConnectedRemoteSessionMetadataKind : IEquatable +public readonly struct SessionLogLevel : 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 ConnectedRemoteSessionMetadataKind(string value) + public SessionLogLevel(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; - /// Remote CLI session. - public static ConnectedRemoteSessionMetadataKind RemoteSession { get; } = new("remote-session"); + /// Informational message. + public static SessionLogLevel Info { get; } = new("info"); - /// GitHub Copilot coding agent session. - public static ConnectedRemoteSessionMetadataKind CodingAgent { get; } = new("coding-agent"); + /// Warning message that may require attention. + public static SessionLogLevel Warning { get; } = new("warning"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ConnectedRemoteSessionMetadataKind left, ConnectedRemoteSessionMetadataKind right) => left.Equals(right); + /// Error message describing a failure. + public static SessionLogLevel Error { get; } = new("error"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ConnectedRemoteSessionMetadataKind left, ConnectedRemoteSessionMetadataKind right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionLogLevel left, SessionLogLevel right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionLogLevel left, SessionLogLevel right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ConnectedRemoteSessionMetadataKind other && Equals(other); + public override bool Equals(object? obj) => obj is SessionLogLevel other && Equals(other); /// - public bool Equals(ConnectedRemoteSessionMetadataKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(SessionLogLevel other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31221,65 +34473,65 @@ public ConnectedRemoteSessionMetadataKind(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 ConnectedRemoteSessionMetadataKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override SessionLogLevel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ConnectedRemoteSessionMetadataKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, SessionLogLevel value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectedRemoteSessionMetadataKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionLogLevel)); } } } -/// Which session sources to include. Defaults to `local` for backward compatibility. +/// When the runtime may run an adapter without an explicit user action. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionSource : IEquatable +public readonly struct ModelProviderAutomaticDiscoveryMode : 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 SessionSource(string value) + public ModelProviderAutomaticDiscoveryMode(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; - /// Return only local sessions. - public static SessionSource Local { get; } = new("local"); + /// The adapter declares that automatic discovery is safe when the other policy fields are satisfied. + public static ModelProviderAutomaticDiscoveryMode Automatic { get; } = new("automatic"); - /// Return only remote sessions. - public static SessionSource Remote { get; } = new("remote"); + /// The adapter may refresh instances the user already configured, but must not scan for new instances automatically. + public static ModelProviderAutomaticDiscoveryMode ConfiguredOnly { get; } = new("configuredOnly"); - /// Return both local and remote sessions. - public static SessionSource All { get; } = new("all"); + /// The adapter must run only after an explicit user action. + public static ModelProviderAutomaticDiscoveryMode Explicit { get; } = new("explicit"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionSource left, SessionSource right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelProviderAutomaticDiscoveryMode left, ModelProviderAutomaticDiscoveryMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionSource left, SessionSource right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelProviderAutomaticDiscoveryMode left, ModelProviderAutomaticDiscoveryMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionSource other && Equals(other); + public override bool Equals(object? obj) => obj is ModelProviderAutomaticDiscoveryMode other && Equals(other); /// - public bool Equals(SessionSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ModelProviderAutomaticDiscoveryMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31287,62 +34539,71 @@ public SessionSource(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 SessionSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ModelProviderAutomaticDiscoveryMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionSource value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ModelProviderAutomaticDiscoveryMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionSource)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelProviderAutomaticDiscoveryMode)); } } } -/// Cursor status: 'ok' means the read succeeded against the requested history; 'expired' means the requested continuation is unavailable. Recovery is endpoint-specific: session.eventLog.read returns a boundary window of remaining active history that may overlap prior pages, while sessions.readPersistedEvents returns an empty terminal page and never switches journal generations. An expired persisted read is not successful completion; a complete persisted snapshot requires cursorStatus 'ok' and hasMore false. +/// Network reach an adapter may use during discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct EventsCursorStatus : IEquatable +public readonly struct ModelProviderDiscoveryNetworkScope : 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 EventsCursorStatus(string value) + public ModelProviderDiscoveryNetworkScope(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 read succeeded against the requested history. - public static EventsCursorStatus Ok { get; } = new("ok"); + /// Discovery does not contact a network service. + public static ModelProviderDiscoveryNetworkScope None { get; } = new("none"); - /// The requested continuation is unavailable; see the endpoint's recovery semantics. - public static EventsCursorStatus Expired { get; } = new("expired"); + /// Discovery is limited to loopback addresses on the local machine. + public static ModelProviderDiscoveryNetworkScope LoopbackOnly { get; } = new("loopbackOnly"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(EventsCursorStatus left, EventsCursorStatus right) => left.Equals(right); + /// Discovery contacts only endpoints the user already configured. + public static ModelProviderDiscoveryNetworkScope ConfiguredEndpointOnly { get; } = new("configuredEndpointOnly"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(EventsCursorStatus left, EventsCursorStatus right) => !(left == right); + /// Discovery may scan or contact the local network. + public static ModelProviderDiscoveryNetworkScope LocalNetwork { get; } = new("localNetwork"); + + /// Discovery may contact remote internet services. + public static ModelProviderDiscoveryNetworkScope Internet { get; } = new("internet"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelProviderDiscoveryNetworkScope left, ModelProviderDiscoveryNetworkScope right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelProviderDiscoveryNetworkScope left, ModelProviderDiscoveryNetworkScope right) => !(left == right); /// - public override bool Equals(object? obj) => obj is EventsCursorStatus other && Equals(other); + public override bool Equals(object? obj) => obj is ModelProviderDiscoveryNetworkScope other && Equals(other); /// - public bool Equals(EventsCursorStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ModelProviderDiscoveryNetworkScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31350,62 +34611,68 @@ public EventsCursorStatus(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 EventsCursorStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ModelProviderDiscoveryNetworkScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, EventsCursorStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ModelProviderDiscoveryNetworkScope value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(EventsCursorStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelProviderDiscoveryNetworkScope)); } } } -/// Direction to page through the session's persisted event history. 'forward' pages from the cursor toward newer events; 'backward' returns the newest window first (tail-first) and pages toward older events. Events within a returned batch are always chronological (oldest-to-newest), even for a backward read. +/// Kind of component that supplied a provider adapter or row. Attribution does not confer authority. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct EventsReadDirection : IEquatable +public readonly struct ModelProviderProvenanceSource : 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 EventsReadDirection(string value) + public ModelProviderProvenanceSource(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; - /// Page from the cursor toward newer events (default). - public static EventsReadDirection Forward { get; } = new("forward"); + /// Built into the runtime. + public static ModelProviderProvenanceSource BuiltIn { get; } = new("builtIn"); - /// Tail-first: return the newest events and page toward older events. - public static EventsReadDirection Backward { get; } = new("backward"); + /// Derived from existing user configuration. + public static ModelProviderProvenanceSource Configured { get; } = new("configured"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(EventsReadDirection left, EventsReadDirection right) => left.Equals(right); + /// Supplied by an extension. + public static ModelProviderProvenanceSource Extension { get; } = new("extension"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(EventsReadDirection left, EventsReadDirection right) => !(left == right); + /// Supplied by another trusted contributor. + public static ModelProviderProvenanceSource Custom { get; } = new("custom"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelProviderProvenanceSource left, ModelProviderProvenanceSource right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelProviderProvenanceSource left, ModelProviderProvenanceSource right) => !(left == right); /// - public override bool Equals(object? obj) => obj is EventsReadDirection other && Equals(other); + public override bool Equals(object? obj) => obj is ModelProviderProvenanceSource other && Equals(other); /// - public bool Equals(EventsReadDirection other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ModelProviderProvenanceSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31413,71 +34680,62 @@ public EventsReadDirection(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 EventsReadDirection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ModelProviderProvenanceSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, EventsReadDirection value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ModelProviderProvenanceSource value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(EventsReadDirection)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelProviderProvenanceSource)); } } } -/// Kind of attention required when status === "attention". Meaningful only when status === "attention". +/// Transport to be used for provider requests. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentRegistryLiveTargetEntryAttentionKind : IEquatable +public readonly struct ProviderEndpointTransport : 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 AgentRegistryLiveTargetEntryAttentionKind(string value) + public ProviderEndpointTransport(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; - /// Session is blocked on an unrecoverable error. - public static AgentRegistryLiveTargetEntryAttentionKind Error { get; } = new("error"); - - /// Session is waiting for a tool-permission decision. - public static AgentRegistryLiveTargetEntryAttentionKind Permission { get; } = new("permission"); - - /// Session is waiting for the user to approve or reject a plan. - public static AgentRegistryLiveTargetEntryAttentionKind ExitPlan { get; } = new("exit_plan"); - - /// Session is waiting on an elicitation prompt. - public static AgentRegistryLiveTargetEntryAttentionKind Elicitation { get; } = new("elicitation"); + /// HTTP request/streaming transport. + public static ProviderEndpointTransport Http { get; } = new("http"); - /// Session is waiting for free-form user input. - public static AgentRegistryLiveTargetEntryAttentionKind UserInput { get; } = new("user_input"); + /// WebSocket transport. + public static ProviderEndpointTransport Websockets { get; } = new("websockets"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentRegistryLiveTargetEntryAttentionKind left, AgentRegistryLiveTargetEntryAttentionKind right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderEndpointTransport left, ProviderEndpointTransport right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentRegistryLiveTargetEntryAttentionKind left, AgentRegistryLiveTargetEntryAttentionKind right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderEndpointTransport left, ProviderEndpointTransport right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentRegistryLiveTargetEntryAttentionKind other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderEndpointTransport other && Equals(other); /// - public bool Equals(AgentRegistryLiveTargetEntryAttentionKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderEndpointTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31485,62 +34743,65 @@ public AgentRegistryLiveTargetEntryAttentionKind(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 AgentRegistryLiveTargetEntryAttentionKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderEndpointTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentRegistryLiveTargetEntryAttentionKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderEndpointTransport value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLiveTargetEntryAttentionKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderEndpointTransport)); } } } -/// Process kind tag for the registry entry. +/// Provider family. Matches the `type` field of a BYOK provider config. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentRegistryLiveTargetEntryKind : IEquatable +public readonly struct ProviderEndpointType : 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 AgentRegistryLiveTargetEntryKind(string value) + public ProviderEndpointType(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; - /// Interactive Copilot CLI exposing a UI server (legacy/normal CLI process). - public static AgentRegistryLiveTargetEntryKind UiServer { get; } = new("ui-server"); + /// OpenAI-compatible endpoint (use the OpenAI client library). + public static ProviderEndpointType Openai { get; } = new("openai"); - /// Headless `--server --managed-server` child spawned by a controller. - public static AgentRegistryLiveTargetEntryKind ManagedServer { get; } = new("managed-server"); + /// Azure OpenAI endpoint (use the OpenAI client library with the Azure base URL). + public static ProviderEndpointType Azure { get; } = new("azure"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentRegistryLiveTargetEntryKind left, AgentRegistryLiveTargetEntryKind right) => left.Equals(right); + /// Anthropic endpoint (use the Anthropic client library). + public static ProviderEndpointType Anthropic { get; } = new("anthropic"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentRegistryLiveTargetEntryKind left, AgentRegistryLiveTargetEntryKind right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderEndpointType left, ProviderEndpointType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderEndpointType left, ProviderEndpointType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentRegistryLiveTargetEntryKind other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderEndpointType other && Equals(other); /// - public bool Equals(AgentRegistryLiveTargetEntryKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderEndpointType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31548,62 +34809,62 @@ public AgentRegistryLiveTargetEntryKind(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 AgentRegistryLiveTargetEntryKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderEndpointType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentRegistryLiveTargetEntryKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderEndpointType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLiveTargetEntryKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderEndpointType)); } } } -/// How the most recent turn ended (clean vs aborted). Lets the renderer distinguish done from done_cancelled. +/// Wire API to be used, when required for the provider type. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentRegistryLiveTargetEntryLastTerminalEvent : IEquatable +public readonly struct ProviderEndpointWireApi : 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 AgentRegistryLiveTargetEntryLastTerminalEvent(string value) + public ProviderEndpointWireApi(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; - /// Last turn ended cleanly (model returned a final assistant message). - public static AgentRegistryLiveTargetEntryLastTerminalEvent TurnEnd { get; } = new("turn_end"); + /// Classic chat-completions request shape. + public static ProviderEndpointWireApi Completions { get; } = new("completions"); - /// Last turn was aborted (e.g. user interrupted). - public static AgentRegistryLiveTargetEntryLastTerminalEvent Abort { get; } = new("abort"); + /// Newer responses request shape. + public static ProviderEndpointWireApi Responses { get; } = new("responses"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentRegistryLiveTargetEntryLastTerminalEvent left, AgentRegistryLiveTargetEntryLastTerminalEvent right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderEndpointWireApi left, ProviderEndpointWireApi right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentRegistryLiveTargetEntryLastTerminalEvent left, AgentRegistryLiveTargetEntryLastTerminalEvent right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderEndpointWireApi left, ProviderEndpointWireApi right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentRegistryLiveTargetEntryLastTerminalEvent other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderEndpointWireApi other && Equals(other); /// - public bool Equals(AgentRegistryLiveTargetEntryLastTerminalEvent other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderEndpointWireApi other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31611,68 +34872,68 @@ public AgentRegistryLiveTargetEntryLastTerminalEvent(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 AgentRegistryLiveTargetEntryLastTerminalEvent Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderEndpointWireApi Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentRegistryLiveTargetEntryLastTerminalEvent value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderEndpointWireApi value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLiveTargetEntryLastTerminalEvent)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderEndpointWireApi)); } } } -/// Coarse lifecycle status of the foreground session. +/// Typed outcome for a provider operation. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentRegistryLiveTargetEntryStatus : IEquatable +public readonly struct ModelProviderOperationOutcomeCode : 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 AgentRegistryLiveTargetEntryStatus(string value) + public ModelProviderOperationOutcomeCode(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; - /// Session is actively processing a turn. - public static AgentRegistryLiveTargetEntryStatus Working { get; } = new("working"); + /// The operation completed successfully; an empty inventory is valid. + public static ModelProviderOperationOutcomeCode Success { get; } = new("success"); - /// Session is idle, waiting for input. - public static AgentRegistryLiveTargetEntryStatus Waiting { get; } = new("waiting"); + /// The provider or instance is absent during discovery, status, or model listing. Distinct from a successful empty inventory. + public static ModelProviderOperationOutcomeCode Absent { get; } = new("absent"); - /// Last turn completed successfully. - public static AgentRegistryLiveTargetEntryStatus Done { get; } = new("done"); + /// The provider is configured or expected but could not be reached. + public static ModelProviderOperationOutcomeCode Unreachable { get; } = new("unreachable"); - /// Session needs user attention (see attentionKind for the specific reason). - public static AgentRegistryLiveTargetEntryStatus Attention { get; } = new("attention"); + /// The operation failed for a reason other than absence or reachability. + public static ModelProviderOperationOutcomeCode Failed { get; } = new("failed"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentRegistryLiveTargetEntryStatus left, AgentRegistryLiveTargetEntryStatus right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelProviderOperationOutcomeCode left, ModelProviderOperationOutcomeCode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentRegistryLiveTargetEntryStatus left, AgentRegistryLiveTargetEntryStatus right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelProviderOperationOutcomeCode left, ModelProviderOperationOutcomeCode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentRegistryLiveTargetEntryStatus other && Equals(other); + public override bool Equals(object? obj) => obj is ModelProviderOperationOutcomeCode other && Equals(other); /// - public bool Equals(AgentRegistryLiveTargetEntryStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ModelProviderOperationOutcomeCode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31680,65 +34941,59 @@ public AgentRegistryLiveTargetEntryStatus(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 AgentRegistryLiveTargetEntryStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ModelProviderOperationOutcomeCode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentRegistryLiveTargetEntryStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ModelProviderOperationOutcomeCode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLiveTargetEntryStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelProviderOperationOutcomeCode)); } } } -/// Categorized reason no canonical process log could be opened. +/// Defines the allowed values. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentRegistryLogCaptureOpenErrorReason : IEquatable +public readonly struct ProtocolAppendMode : 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 AgentRegistryLogCaptureOpenErrorReason(string value) + public ProtocolAppendMode(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; - /// Filesystem permission denied opening the log file. - public static AgentRegistryLogCaptureOpenErrorReason Permission { get; } = new("permission"); - - /// No space left on device. - public static AgentRegistryLogCaptureOpenErrorReason DiskFull { get; } = new("disk_full"); - - /// Other / uncategorized open failure. - public static AgentRegistryLogCaptureOpenErrorReason Other { get; } = new("other"); + /// Gets the append value. + public static ProtocolAppendMode Append { get; } = new("append"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentRegistryLogCaptureOpenErrorReason left, AgentRegistryLogCaptureOpenErrorReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProtocolAppendMode left, ProtocolAppendMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentRegistryLogCaptureOpenErrorReason left, AgentRegistryLogCaptureOpenErrorReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProtocolAppendMode left, ProtocolAppendMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentRegistryLogCaptureOpenErrorReason other && Equals(other); + public override bool Equals(object? obj) => obj is ProtocolAppendMode other && Equals(other); /// - public bool Equals(AgentRegistryLogCaptureOpenErrorReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProtocolAppendMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31746,71 +35001,59 @@ public AgentRegistryLogCaptureOpenErrorReason(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 AgentRegistryLogCaptureOpenErrorReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProtocolAppendMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentRegistryLogCaptureOpenErrorReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProtocolAppendMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistryLogCaptureOpenErrorReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProtocolAppendMode)); } } } -/// Which parameter field was invalid. Omitted when the rejection is not field-specific. +/// Defines the allowed values. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentRegistrySpawnValidationErrorField : IEquatable +public readonly struct ProtocolReplaceMode : 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 AgentRegistrySpawnValidationErrorField(string value) + public ProtocolReplaceMode(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 cwd parameter. - public static AgentRegistrySpawnValidationErrorField Cwd { get; } = new("cwd"); - - /// The session name parameter. - public static AgentRegistrySpawnValidationErrorField Name { get; } = new("name"); - - /// The agentName parameter. - public static AgentRegistrySpawnValidationErrorField AgentName { get; } = new("agentName"); - - /// The model parameter. - public static AgentRegistrySpawnValidationErrorField Model { get; } = new("model"); - - /// The permissionMode parameter. - public static AgentRegistrySpawnValidationErrorField PermissionMode { get; } = new("permissionMode"); + /// Gets the replace value. + public static ProtocolReplaceMode Replace { get; } = new("replace"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentRegistrySpawnValidationErrorField left, AgentRegistrySpawnValidationErrorField right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProtocolReplaceMode left, ProtocolReplaceMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentRegistrySpawnValidationErrorField left, AgentRegistrySpawnValidationErrorField right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProtocolReplaceMode left, ProtocolReplaceMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentRegistrySpawnValidationErrorField other && Equals(other); + public override bool Equals(object? obj) => obj is ProtocolReplaceMode other && Equals(other); /// - public bool Equals(AgentRegistrySpawnValidationErrorField other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProtocolReplaceMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31818,74 +35061,59 @@ public AgentRegistrySpawnValidationErrorField(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 AgentRegistrySpawnValidationErrorField Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProtocolReplaceMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentRegistrySpawnValidationErrorField value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProtocolReplaceMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistrySpawnValidationErrorField)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProtocolReplaceMode)); } } } -/// Categorized reason for the rejection. Low-cardinality enum so telemetry can aggregate by reason without leaking raw paths or agent/model names. +/// Defines the allowed values. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentRegistrySpawnValidationErrorReason : IEquatable +public readonly struct ProtocolCustomizeMode : 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 AgentRegistrySpawnValidationErrorReason(string value) + public ProtocolCustomizeMode(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; - /// Provided cwd does not exist on disk. - public static AgentRegistrySpawnValidationErrorReason CwdNotFound { get; } = new("cwd-not-found"); - - /// Provided cwd exists but is not a directory. - public static AgentRegistrySpawnValidationErrorReason CwdNotDirectory { get; } = new("cwd-not-directory"); - - /// Session name failed validateSessionName. - public static AgentRegistrySpawnValidationErrorReason InvalidName { get; } = new("invalid-name"); - - /// Requested agent name was not found in builtin or custom agents. - public static AgentRegistrySpawnValidationErrorReason UnknownAgent { get; } = new("unknown-agent"); - - /// Requested model is not available to this session. - public static AgentRegistrySpawnValidationErrorReason UnknownModel { get; } = new("unknown-model"); - - /// Caller asked for permissionMode='yolo' but the controller is not currently in allow-all mode. - public static AgentRegistrySpawnValidationErrorReason YoloNotAllowed { get; } = new("yolo-not-allowed"); + /// Gets the customize value. + public static ProtocolCustomizeMode Customize { get; } = new("customize"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentRegistrySpawnValidationErrorReason left, AgentRegistrySpawnValidationErrorReason right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProtocolCustomizeMode left, ProtocolCustomizeMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentRegistrySpawnValidationErrorReason left, AgentRegistrySpawnValidationErrorReason right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProtocolCustomizeMode left, ProtocolCustomizeMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentRegistrySpawnValidationErrorReason other && Equals(other); + public override bool Equals(object? obj) => obj is ProtocolCustomizeMode other && Equals(other); /// - public bool Equals(AgentRegistrySpawnValidationErrorReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProtocolCustomizeMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31893,62 +35121,68 @@ public AgentRegistrySpawnValidationErrorReason(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 AgentRegistrySpawnValidationErrorReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProtocolCustomizeMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentRegistrySpawnValidationErrorReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProtocolCustomizeMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistrySpawnValidationErrorReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProtocolCustomizeMode)); } } } -/// Permission posture for the new session. 'yolo' requires the controller-local session to currently be in allow-all mode. +/// Defines the allowed values. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct AgentRegistrySpawnPermissionMode : IEquatable +public readonly struct ProtocolStaticSectionAction : 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 AgentRegistrySpawnPermissionMode(string value) + public ProtocolStaticSectionAction(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; - /// Standard permission posture (prompts for each request). - public static AgentRegistrySpawnPermissionMode Default { get; } = new("default"); + /// Replace the section content. + public static ProtocolStaticSectionAction Replace { get; } = new("replace"); - /// Full allow-all (requires the controller-local session to currently be in allow-all mode). - public static AgentRegistrySpawnPermissionMode Yolo { get; } = new("yolo"); + /// Remove the section content. + public static ProtocolStaticSectionAction Remove { get; } = new("remove"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(AgentRegistrySpawnPermissionMode left, AgentRegistrySpawnPermissionMode right) => left.Equals(right); + /// Append content to the section. + public static ProtocolStaticSectionAction Append { get; } = new("append"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(AgentRegistrySpawnPermissionMode left, AgentRegistrySpawnPermissionMode right) => !(left == right); + /// Prepend content to the section. + public static ProtocolStaticSectionAction Prepend { get; } = new("prepend"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProtocolStaticSectionAction left, ProtocolStaticSectionAction right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProtocolStaticSectionAction left, ProtocolStaticSectionAction right) => !(left == right); /// - public override bool Equals(object? obj) => obj is AgentRegistrySpawnPermissionMode other && Equals(other); + public override bool Equals(object? obj) => obj is ProtocolStaticSectionAction other && Equals(other); /// - public bool Equals(AgentRegistrySpawnPermissionMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProtocolStaticSectionAction other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -31956,65 +35190,62 @@ public AgentRegistrySpawnPermissionMode(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 AgentRegistrySpawnPermissionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProtocolStaticSectionAction Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, AgentRegistrySpawnPermissionMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProtocolStaticSectionAction value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AgentRegistrySpawnPermissionMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProtocolStaticSectionAction)); } } } -/// How far OneAuth may go to acquire the requested token. +/// Whether a planned configuration entry is new or already present in the session registry. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct EntraTokenInteraction : IEquatable +public readonly struct ModelProviderConfigurationDisposition : 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 EntraTokenInteraction(string value) + public ModelProviderConfigurationDisposition(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; - /// Acquire the token without any user interaction, failing if interaction would be required. - public static EntraTokenInteraction Silent { get; } = new("silent"); - - /// Allow interactive acquisition, prompting the user only when a cached or silent token is unavailable. - public static EntraTokenInteraction Interactive { get; } = new("interactive"); + /// No matching entry is registered; the caller should add the entry. + public static ModelProviderConfigurationDisposition Create { get; } = new("create"); - /// Always prompt interactively, bypassing any cached or silently-refreshable token. - public static EntraTokenInteraction ForceInteractive { get; } = new("force-interactive"); + /// An equivalent entry is already registered; the caller should reuse it rather than adding a duplicate. + public static ModelProviderConfigurationDisposition AlreadyConfigured { get; } = new("alreadyConfigured"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(EntraTokenInteraction left, EntraTokenInteraction right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelProviderConfigurationDisposition left, ModelProviderConfigurationDisposition right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(EntraTokenInteraction left, EntraTokenInteraction right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelProviderConfigurationDisposition left, ModelProviderConfigurationDisposition right) => !(left == right); /// - public override bool Equals(object? obj) => obj is EntraTokenInteraction other && Equals(other); + public override bool Equals(object? obj) => obj is ModelProviderConfigurationDisposition other && Equals(other); /// - public bool Equals(EntraTokenInteraction other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ModelProviderConfigurationDisposition other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -32022,68 +35253,77 @@ public EntraTokenInteraction(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 EntraTokenInteraction Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ModelProviderConfigurationDisposition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, EntraTokenInteraction value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ModelProviderConfigurationDisposition value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(EntraTokenInteraction)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelProviderConfigurationDisposition)); } } } -/// The UI mode the agent was in when this message was sent. Defaults to the session's current mode. +/// The product serving the model, reported in telemetry as `model_provider`. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SendAgentMode : IEquatable +public readonly struct ProviderConfigModelProvider : 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 SendAgentMode(string value) + public ProviderConfigModelProvider(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 agent is responding interactively to the user. - public static SendAgentMode Interactive { get; } = new("interactive"); + /// OpenAI API. + public static ProviderConfigModelProvider Openai { get; } = new("openai"); - /// The agent is preparing a plan before making changes. - public static SendAgentMode Plan { get; } = new("plan"); + /// Anthropic API. + public static ProviderConfigModelProvider Anthropic { get; } = new("anthropic"); - /// The agent is working autonomously toward task completion. - public static SendAgentMode Autopilot { get; } = new("autopilot"); + /// Azure OpenAI Service. + public static ProviderConfigModelProvider AzureOpenai { get; } = new("azure_openai"); - /// The agent is in shell-focused UI mode. - public static SendAgentMode Shell { get; } = new("shell"); + /// Ollama. + public static ProviderConfigModelProvider Ollama { get; } = new("ollama"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SendAgentMode left, SendAgentMode right) => left.Equals(right); + /// LM Studio. + public static ProviderConfigModelProvider LmStudio { get; } = new("lm_studio"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SendAgentMode left, SendAgentMode right) => !(left == right); + /// Foundry Local. + public static ProviderConfigModelProvider FoundryLocal { get; } = new("foundry_local"); + + /// llama.cpp server. + public static ProviderConfigModelProvider LlamaCpp { get; } = new("llama_cpp"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderConfigModelProvider left, ProviderConfigModelProvider right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderConfigModelProvider left, ProviderConfigModelProvider right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SendAgentMode other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderConfigModelProvider other && Equals(other); /// - public bool Equals(SendAgentMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderConfigModelProvider other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -32091,62 +35331,62 @@ public SendAgentMode(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 SendAgentMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderConfigModelProvider Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SendAgentMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderConfigModelProvider value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SendAgentMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderConfigModelProvider)); } } } -/// How to deliver the message. `enqueue` (default) appends to the message queue. `immediate` interjects during an in-progress turn. +/// Provider transport. Defaults to "http". [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SendMode : IEquatable +public readonly struct ProviderConfigTransport : 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 SendMode(string value) + public ProviderConfigTransport(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; - /// Append the message to the normal session queue. - public static SendMode Enqueue { get; } = new("enqueue"); + /// HTTP request/streaming transport. + public static ProviderConfigTransport Http { get; } = new("http"); - /// Interject the message during the in-progress turn. - public static SendMode Immediate { get; } = new("immediate"); + /// WebSocket transport. + public static ProviderConfigTransport Websockets { get; } = new("websockets"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SendMode left, SendMode right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderConfigTransport left, ProviderConfigTransport right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SendMode left, SendMode right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderConfigTransport left, ProviderConfigTransport right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SendMode other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderConfigTransport other && Equals(other); /// - public bool Equals(SendMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderConfigTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -32154,65 +35394,65 @@ public SendMode(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 SendMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderConfigTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SendMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderConfigTransport value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SendMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderConfigTransport)); } } } -/// Log severity level. Determines how the message is displayed in the timeline. Defaults to "info". +/// Provider type. Defaults to "openai" for generic OpenAI-compatible APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct SessionLogLevel : IEquatable +public readonly struct ProviderConfigType : 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 SessionLogLevel(string value) + public ProviderConfigType(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; - /// Informational message. - public static SessionLogLevel Info { get; } = new("info"); + /// Generic OpenAI-compatible API. + public static ProviderConfigType Openai { get; } = new("openai"); - /// Warning message that may require attention. - public static SessionLogLevel Warning { get; } = new("warning"); + /// Azure OpenAI Service endpoint. + public static ProviderConfigType Azure { get; } = new("azure"); - /// Error message describing a failure. - public static SessionLogLevel Error { get; } = new("error"); + /// Anthropic API endpoint. + public static ProviderConfigType Anthropic { get; } = new("anthropic"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionLogLevel left, SessionLogLevel right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderConfigType left, ProviderConfigType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionLogLevel left, SessionLogLevel right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderConfigType left, ProviderConfigType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is SessionLogLevel other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderConfigType other && Equals(other); /// - public bool Equals(SessionLogLevel other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderConfigType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -32220,20 +35460,83 @@ public SessionLogLevel(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 SessionLogLevel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderConfigType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, SessionLogLevel value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderConfigType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionLogLevel)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderConfigType)); + } + } +} + + +/// Wire API format (openai/azure only). Defaults to "completions". +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ProviderConfigWireApi : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ProviderConfigWireApi(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// OpenAI Chat Completions wire format. + public static ProviderConfigWireApi Completions { get; } = new("completions"); + + /// OpenAI Responses API wire format. + public static ProviderConfigWireApi Responses { get; } = new("responses"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderConfigWireApi left, ProviderConfigWireApi right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderConfigWireApi left, ProviderConfigWireApi right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ProviderConfigWireApi other && Equals(other); + + /// + public bool Equals(ProviderConfigWireApi 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 ProviderConfigWireApi Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ProviderConfigWireApi value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderConfigWireApi)); } } } @@ -32443,87 +35746,6 @@ public override void Write(Utf8JsonWriter writer, PermissionDecisionSurface valu } -/// 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 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)); - } - } -} - - /// The provider kind stamped on a signed-in account. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -32662,7 +35884,7 @@ public override void Write(Utf8JsonWriter writer, LoginProviderKind value, JsonS } -/// Terminal disposition of a login persistence attempt. +/// Disposition of a login attempt, including pending user decisions. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -32682,12 +35904,15 @@ public AuthLoginResultStatus(string value) /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// The credential was persisted and the account is signed in. + /// The credential was persisted and the selected account is signed in. public static AuthLoginResultStatus Completed { get; } = new("completed"); /// Persistence needs explicit consent to store the token in plaintext. public static AuthLoginResultStatus NeedsPlaintextConsent { get; } = new("needs-plaintext-consent"); + /// Credentials are saved; select an account using a returned selectionId as advance input to complete sign-in. + public static AuthLoginResultStatus NeedsAccountSelection { get; } = new("needs-account-selection"); + /// The user declined plaintext persistence. public static AuthLoginResultStatus Declined { get; } = new("declined"); @@ -33811,376 +37036,46 @@ public override void Write(Utf8JsonWriter writer, AutopilotObjectiveStatus value } -/// Whether task execution is synchronously awaited or managed in the background. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct TaskExecutionMode : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public TaskExecutionMode(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// The task was started with synchronous waiting. - public static TaskExecutionMode Sync { get; } = new("sync"); - - /// The task is managed in the background. - public static TaskExecutionMode Background { get; } = new("background"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskExecutionMode left, TaskExecutionMode right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskExecutionMode left, TaskExecutionMode right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is TaskExecutionMode other && Equals(other); - - /// - public bool Equals(TaskExecutionMode 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 TaskExecutionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, TaskExecutionMode value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskExecutionMode)); - } - } -} - - -/// Current lifecycle status of the task. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct TaskStatus : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public TaskStatus(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// The task is actively executing. - public static TaskStatus Running { get; } = new("running"); - - /// The task is waiting for additional input. - public static TaskStatus Idle { get; } = new("idle"); - - /// The task finished successfully. - public static TaskStatus Completed { get; } = new("completed"); - - /// The task finished with an error. - public static TaskStatus Failed { get; } = new("failed"); - - /// The task was cancelled before completion. - public static TaskStatus Cancelled { get; } = new("cancelled"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskStatus left, TaskStatus right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskStatus left, TaskStatus right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is TaskStatus other && Equals(other); - - /// - public bool Equals(TaskStatus 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 TaskStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, TaskStatus value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskStatus)); - } - } -} - - -/// Client-owned tasks always execute outside the runtime in background mode. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct TaskClientExecutionMode : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public TaskClientExecutionMode(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Gets the background value. - public static TaskClientExecutionMode Background { get; } = new("background"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskClientExecutionMode left, TaskClientExecutionMode right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskClientExecutionMode left, TaskClientExecutionMode right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is TaskClientExecutionMode other && Equals(other); - - /// - public bool Equals(TaskClientExecutionMode 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 TaskClientExecutionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, TaskClientExecutionMode value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientExecutionMode)); - } - } -} - - -/// Connection class owning a client task. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct TaskClientOwnerKind : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public TaskClientOwnerKind(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// A discovered extension connection owns the task. - public static TaskClientOwnerKind Extension { get; } = new("extension"); - - /// A generic SDK connection owns the task. - public static TaskClientOwnerKind Sdk { get; } = new("sdk"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskClientOwnerKind left, TaskClientOwnerKind right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskClientOwnerKind left, TaskClientOwnerKind right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is TaskClientOwnerKind other && Equals(other); - - /// - public bool Equals(TaskClientOwnerKind 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 TaskClientOwnerKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, TaskClientOwnerKind value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientOwnerKind)); - } - } -} - - -/// Presence of the task's bound join. +/// Result of reloading a customization component. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct TaskClientOwnerPresence : IEquatable +public readonly struct CustomizationReloadStatus : 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 TaskClientOwnerPresence(string value) + public CustomizationReloadStatus(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 bound session join is connected. - public static TaskClientOwnerPresence Connected { get; } = new("connected"); + /// The component was refreshed successfully. + public static CustomizationReloadStatus Reloaded { get; } = new("reloaded"); - /// The bound session join is disconnected. - public static TaskClientOwnerPresence Disconnected { get; } = new("disconnected"); + /// The component was not configured, loaded, or eligible for refresh. + public static CustomizationReloadStatus Skipped { get; } = new("skipped"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskClientOwnerPresence left, TaskClientOwnerPresence right) => left.Equals(right); + /// The component could not be refreshed; other components may still reload. + public static CustomizationReloadStatus Failed { get; } = new("failed"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskClientOwnerPresence left, TaskClientOwnerPresence right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CustomizationReloadStatus left, CustomizationReloadStatus right) => left.Equals(right); - /// - public override bool Equals(object? obj) => obj is TaskClientOwnerPresence other && Equals(other); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CustomizationReloadStatus left, CustomizationReloadStatus right) => !(left == right); /// - public bool Equals(TaskClientOwnerPresence other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public override bool Equals(object? obj) => obj is CustomizationReloadStatus other && Equals(other); /// - 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 TaskClientOwnerPresence Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, TaskClientOwnerPresence value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientOwnerPresence)); - } - } -} - - -/// Lifecycle status of a client-owned task. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct TaskClientStatus : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public TaskClientStatus(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// The external owner is actively working. - public static TaskClientStatus Running { get; } = new("running"); - - /// The external owner is connected but waiting. - public static TaskClientStatus Idle { get; } = new("idle"); - - /// The owner reported successful completion. - public static TaskClientStatus Completed { get; } = new("completed"); - - /// The owner reported failure. - public static TaskClientStatus Failed { get; } = new("failed"); - - /// The owner reported or confirmed cancellation. - public static TaskClientStatus Cancelled { get; } = new("cancelled"); - - /// The bound owner join disappeared; external executor state is unknown. - public static TaskClientStatus Orphaned { get; } = new("orphaned"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskClientStatus left, TaskClientStatus right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskClientStatus left, TaskClientStatus right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is TaskClientStatus other && Equals(other); - - /// - public bool Equals(TaskClientStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CustomizationReloadStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34188,185 +37083,80 @@ public TaskClientStatus(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 TaskClientStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CustomizationReloadStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, TaskClientStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CustomizationReloadStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CustomizationReloadStatus)); } } } -/// Whether the shell runs inside a managed PTY session or as an independent background process. +/// Component of session customization discovery. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct TaskShellInfoAttachmentMode : IEquatable +public readonly struct CustomizationReloadSubsystem : 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 TaskShellInfoAttachmentMode(string value) + public CustomizationReloadSubsystem(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 shell runs in a managed PTY session. - public static TaskShellInfoAttachmentMode Attached { get; } = new("attached"); + /// Repository metadata and working-directory context. + public static CustomizationReloadSubsystem RepositoryContext { get; } = new("repositoryContext"); - /// The shell runs as an independent background process. - public static TaskShellInfoAttachmentMode Detached { get; } = new("detached"); + /// Session instructions and their cached dynamic context. + public static CustomizationReloadSubsystem Instructions { get; } = new("instructions"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskShellInfoAttachmentMode left, TaskShellInfoAttachmentMode right) => left.Equals(right); + /// Discovered plugin configuration. + public static CustomizationReloadSubsystem Plugins { get; } = new("plugins"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskShellInfoAttachmentMode left, TaskShellInfoAttachmentMode right) => !(left == right); + /// Configured session and plugin hooks. + public static CustomizationReloadSubsystem Hooks { get; } = new("hooks"); - /// - public override bool Equals(object? obj) => obj is TaskShellInfoAttachmentMode other && Equals(other); + /// Discovered skills. + public static CustomizationReloadSubsystem Skills { get; } = new("skills"); - /// - public bool Equals(TaskShellInfoAttachmentMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + /// Discovered custom agents. + public static CustomizationReloadSubsystem Agents { get; } = new("agents"); - /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + /// Loaded MCP server configuration. + public static CustomizationReloadSubsystem Mcp { get; } = new("mcp"); - /// - public override string ToString() => Value; - - /// Provides a for serializing instances. - [EditorBrowsable(EditorBrowsableState.Never)] - public sealed class Converter : JsonConverter - { - /// - public override TaskShellInfoAttachmentMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, TaskShellInfoAttachmentMode value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskShellInfoAttachmentMode)); - } - } -} - - -/// Discriminator for a client-owned task. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct TaskClientType : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public TaskClientType(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// Gets the client value. - public static TaskClientType Client { get; } = new("client"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskClientType left, TaskClientType right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskClientType left, TaskClientType right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is TaskClientType other && Equals(other); - - /// - public bool Equals(TaskClientType 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 TaskClientType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, TaskClientType value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientType)); - } - } -} - - -/// Active status a client owner may publish with a progress update. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct TaskClientActiveStatus : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public TaskClientActiveStatus(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// The external owner is actively working. - public static TaskClientActiveStatus Running { get; } = new("running"); + /// Configured session extensions. + public static CustomizationReloadSubsystem Extensions { get; } = new("extensions"); - /// The external owner is connected but waiting. - public static TaskClientActiveStatus Idle { get; } = new("idle"); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(CustomizationReloadSubsystem left, CustomizationReloadSubsystem right) => left.Equals(right); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(TaskClientActiveStatus left, TaskClientActiveStatus right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(TaskClientActiveStatus left, TaskClientActiveStatus right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(CustomizationReloadSubsystem left, CustomizationReloadSubsystem right) => !(left == right); /// - public override bool Equals(object? obj) => obj is TaskClientActiveStatus other && Equals(other); + public override bool Equals(object? obj) => obj is CustomizationReloadSubsystem other && Equals(other); /// - public bool Equals(TaskClientActiveStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(CustomizationReloadSubsystem other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34374,62 +37164,62 @@ public TaskClientActiveStatus(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 TaskClientActiveStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override CustomizationReloadSubsystem Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, TaskClientActiveStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, CustomizationReloadSubsystem value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientActiveStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(CustomizationReloadSubsystem)); } } } -/// Consumer allowed to call an MCP tool. +/// Whether task execution is synchronously awaited or managed in the background. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpToolUiVisibility : IEquatable +public readonly struct TaskExecutionMode : 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 McpToolUiVisibility(string value) + public TaskExecutionMode(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 model may call the tool. - public static McpToolUiVisibility Model { get; } = new("model"); + /// The task was started with synchronous waiting. + public static TaskExecutionMode Sync { get; } = new("sync"); - /// An MCP App view may call the tool. - public static McpToolUiVisibility App { get; } = new("app"); + /// The task is managed in the background. + public static TaskExecutionMode Background { get; } = new("background"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpToolUiVisibility left, McpToolUiVisibility right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskExecutionMode left, TaskExecutionMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpToolUiVisibility left, McpToolUiVisibility right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskExecutionMode left, TaskExecutionMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpToolUiVisibility other && Equals(other); + public override bool Equals(object? obj) => obj is TaskExecutionMode other && Equals(other); /// - public bool Equals(McpToolUiVisibility other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(TaskExecutionMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34437,65 +37227,71 @@ public McpToolUiVisibility(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 McpToolUiVisibility Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TaskExecutionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpToolUiVisibility value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TaskExecutionMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpToolUiVisibility)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskExecutionMode)); } } } -/// 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. +/// Current lifecycle status of the task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpSamplingExecutionAction : IEquatable +public readonly struct TaskStatus : 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 McpSamplingExecutionAction(string value) + public TaskStatus(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 sampling inference completed and produced a result. - public static McpSamplingExecutionAction Success { get; } = new("success"); + /// The task is actively executing. + public static TaskStatus Running { get; } = new("running"); - /// The sampling inference failed or was rejected. - public static McpSamplingExecutionAction Failure { get; } = new("failure"); + /// The task is waiting for additional input. + public static TaskStatus Idle { get; } = new("idle"); - /// The sampling inference was cancelled before completion. - public static McpSamplingExecutionAction Cancelled { get; } = new("cancelled"); + /// The task finished successfully. + public static TaskStatus Completed { get; } = new("completed"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpSamplingExecutionAction left, McpSamplingExecutionAction right) => left.Equals(right); + /// The task finished with an error. + public static TaskStatus Failed { get; } = new("failed"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpSamplingExecutionAction left, McpSamplingExecutionAction right) => !(left == right); + /// The task was cancelled before completion. + public static TaskStatus Cancelled { get; } = new("cancelled"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskStatus left, TaskStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskStatus left, TaskStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpSamplingExecutionAction other && Equals(other); + public override bool Equals(object? obj) => obj is TaskStatus other && Equals(other); /// - public bool Equals(McpSamplingExecutionAction other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(TaskStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34503,62 +37299,59 @@ public McpSamplingExecutionAction(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 McpSamplingExecutionAction Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TaskStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpSamplingExecutionAction value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TaskStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpSamplingExecutionAction)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskStatus)); } } } -/// 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". +/// Client-owned tasks always execute outside the runtime in background mode. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpSetEnvValueModeDetails : IEquatable +public readonly struct TaskClientExecutionMode : 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 McpSetEnvValueModeDetails(string value) + public TaskClientExecutionMode(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; - /// Treat MCP server environment values as literal strings. - public static McpSetEnvValueModeDetails Direct { get; } = new("direct"); - - /// Treat MCP server environment values as host-side references to resolve before launch. - public static McpSetEnvValueModeDetails Indirect { get; } = new("indirect"); + /// Gets the background value. + public static TaskClientExecutionMode Background { get; } = new("background"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpSetEnvValueModeDetails left, McpSetEnvValueModeDetails right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskClientExecutionMode left, TaskClientExecutionMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpSetEnvValueModeDetails left, McpSetEnvValueModeDetails right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskClientExecutionMode left, TaskClientExecutionMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpSetEnvValueModeDetails other && Equals(other); + public override bool Equals(object? obj) => obj is TaskClientExecutionMode other && Equals(other); /// - public bool Equals(McpSetEnvValueModeDetails other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(TaskClientExecutionMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34566,62 +37359,62 @@ public McpSetEnvValueModeDetails(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 McpSetEnvValueModeDetails Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TaskClientExecutionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpSetEnvValueModeDetails value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TaskClientExecutionMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpSetEnvValueModeDetails)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientExecutionMode)); } } } -/// Outcome of starting the original prepared owned login. +/// Connection class owning a client task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpOwnedOauthLoginStatus : IEquatable +public readonly struct TaskClientOwnerKind : 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 McpOwnedOauthLoginStatus(string value) + public TaskClientOwnerKind(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 original requester may open the returned authorisation URL. - public static McpOwnedOauthLoginStatus AwaitingBrowser { get; } = new("awaiting-browser"); + /// A discovered extension connection owns the task. + public static TaskClientOwnerKind Extension { get; } = new("extension"); - /// Cached credentials were accepted and the original server finished reconnecting. - public static McpOwnedOauthLoginStatus Connected { get; } = new("connected"); + /// A generic SDK connection owns the task. + public static TaskClientOwnerKind Sdk { get; } = new("sdk"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpOwnedOauthLoginStatus left, McpOwnedOauthLoginStatus right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskClientOwnerKind left, TaskClientOwnerKind right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpOwnedOauthLoginStatus left, McpOwnedOauthLoginStatus right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskClientOwnerKind left, TaskClientOwnerKind right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpOwnedOauthLoginStatus other && Equals(other); + public override bool Equals(object? obj) => obj is TaskClientOwnerKind other && Equals(other); /// - public bool Equals(McpOwnedOauthLoginStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(TaskClientOwnerKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34629,62 +37422,62 @@ public McpOwnedOauthLoginStatus(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 McpOwnedOauthLoginStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TaskClientOwnerKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpOwnedOauthLoginStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TaskClientOwnerKind value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpOwnedOauthLoginStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientOwnerKind)); } } } -/// OAuth grant type override for this login. +/// Presence of the task's bound join. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpOauthLoginGrantType : IEquatable +public readonly struct TaskClientOwnerPresence : 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 McpOauthLoginGrantType(string value) + public TaskClientOwnerPresence(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; - /// Interactive browser-based OAuth flow using an authorization code, typically with PKCE. - public static McpOauthLoginGrantType AuthorizationCode { get; } = new("authorization_code"); + /// The bound session join is connected. + public static TaskClientOwnerPresence Connected { get; } = new("connected"); - /// Headless OAuth flow where a confidential client authenticates directly with a client secret. - public static McpOauthLoginGrantType ClientCredentials { get; } = new("client_credentials"); + /// The bound session join is disconnected. + public static TaskClientOwnerPresence Disconnected { get; } = new("disconnected"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpOauthLoginGrantType left, McpOauthLoginGrantType right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskClientOwnerPresence left, TaskClientOwnerPresence right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpOauthLoginGrantType left, McpOauthLoginGrantType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskClientOwnerPresence left, TaskClientOwnerPresence right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpOauthLoginGrantType other && Equals(other); + public override bool Equals(object? obj) => obj is TaskClientOwnerPresence other && Equals(other); /// - public bool Equals(McpOauthLoginGrantType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(TaskClientOwnerPresence other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34692,65 +37485,74 @@ public McpOauthLoginGrantType(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 McpOauthLoginGrantType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TaskClientOwnerPresence Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpOauthLoginGrantType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TaskClientOwnerPresence value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpOauthLoginGrantType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientOwnerPresence)); } } } -/// Why a passive MCP OAuth probe determined authentication is needed. +/// Lifecycle status of a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpOauthProbeNeedsAuthReason : IEquatable +public readonly struct TaskClientStatus : 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 McpOauthProbeNeedsAuthReason(string value) + public TaskClientStatus(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; - /// No token was sent and the server requires authentication. - public static McpOauthProbeNeedsAuthReason Initial { get; } = new("initial"); + /// The external owner is actively working. + public static TaskClientStatus Running { get; } = new("running"); - /// A cached token was sent and rejected. - public static McpOauthProbeNeedsAuthReason Refresh { get; } = new("refresh"); + /// The external owner is connected but waiting. + public static TaskClientStatus Idle { get; } = new("idle"); - /// The server returned a 403 insufficient_scope challenge, indicating additional scopes or audience are needed. - public static McpOauthProbeNeedsAuthReason Upscope { get; } = new("upscope"); + /// The owner reported successful completion. + public static TaskClientStatus Completed { get; } = new("completed"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpOauthProbeNeedsAuthReason left, McpOauthProbeNeedsAuthReason right) => left.Equals(right); + /// The owner reported failure. + public static TaskClientStatus Failed { get; } = new("failed"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpOauthProbeNeedsAuthReason left, McpOauthProbeNeedsAuthReason right) => !(left == right); + /// The owner reported or confirmed cancellation. + public static TaskClientStatus Cancelled { get; } = new("cancelled"); + + /// The bound owner join disappeared; external executor state is unknown. + public static TaskClientStatus Orphaned { get; } = new("orphaned"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskClientStatus left, TaskClientStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskClientStatus left, TaskClientStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpOauthProbeNeedsAuthReason other && Equals(other); + public override bool Equals(object? obj) => obj is TaskClientStatus other && Equals(other); /// - public bool Equals(McpOauthProbeNeedsAuthReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(TaskClientStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34758,65 +37560,62 @@ public McpOauthProbeNeedsAuthReason(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 McpOauthProbeNeedsAuthReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TaskClientStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpOauthProbeNeedsAuthReason value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TaskClientStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpOauthProbeNeedsAuthReason)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientStatus)); } } } -/// Allowed values for the `McpAppsSetHostContextDetailsAvailableDisplayMode` enumeration. +/// Whether the shell runs inside a managed PTY session or as an independent background process. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpAppsSetHostContextDetailsAvailableDisplayMode : IEquatable +public readonly struct TaskShellInfoAttachmentMode : 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 McpAppsSetHostContextDetailsAvailableDisplayMode(string value) + public TaskShellInfoAttachmentMode(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; - /// Rendered inline within the host conversation surface. - public static McpAppsSetHostContextDetailsAvailableDisplayMode Inline { get; } = new("inline"); - - /// Rendered as a fullscreen overlay. - public static McpAppsSetHostContextDetailsAvailableDisplayMode Fullscreen { get; } = new("fullscreen"); + /// The shell runs in a managed PTY session. + public static TaskShellInfoAttachmentMode Attached { get; } = new("attached"); - /// Rendered as a picture-in-picture floating panel. - public static McpAppsSetHostContextDetailsAvailableDisplayMode Pip { get; } = new("pip"); + /// The shell runs as an independent background process. + public static TaskShellInfoAttachmentMode Detached { get; } = new("detached"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpAppsSetHostContextDetailsAvailableDisplayMode left, McpAppsSetHostContextDetailsAvailableDisplayMode right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskShellInfoAttachmentMode left, TaskShellInfoAttachmentMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpAppsSetHostContextDetailsAvailableDisplayMode left, McpAppsSetHostContextDetailsAvailableDisplayMode right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskShellInfoAttachmentMode left, TaskShellInfoAttachmentMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpAppsSetHostContextDetailsAvailableDisplayMode other && Equals(other); + public override bool Equals(object? obj) => obj is TaskShellInfoAttachmentMode other && Equals(other); /// - public bool Equals(McpAppsSetHostContextDetailsAvailableDisplayMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(TaskShellInfoAttachmentMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34824,65 +37623,59 @@ public McpAppsSetHostContextDetailsAvailableDisplayMode(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 McpAppsSetHostContextDetailsAvailableDisplayMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TaskShellInfoAttachmentMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpAppsSetHostContextDetailsAvailableDisplayMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TaskShellInfoAttachmentMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsSetHostContextDetailsAvailableDisplayMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskShellInfoAttachmentMode)); } } } -/// Current display mode (SEP-1865). +/// Discriminator for a client-owned task. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpAppsSetHostContextDetailsDisplayMode : IEquatable +public readonly struct TaskClientType : 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 McpAppsSetHostContextDetailsDisplayMode(string value) + public TaskClientType(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; - /// Rendered inline within the host conversation surface. - public static McpAppsSetHostContextDetailsDisplayMode Inline { get; } = new("inline"); - - /// Rendered as a fullscreen overlay. - public static McpAppsSetHostContextDetailsDisplayMode Fullscreen { get; } = new("fullscreen"); - - /// Rendered as a picture-in-picture floating panel. - public static McpAppsSetHostContextDetailsDisplayMode Pip { get; } = new("pip"); + /// Gets the client value. + public static TaskClientType Client { get; } = new("client"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpAppsSetHostContextDetailsDisplayMode left, McpAppsSetHostContextDetailsDisplayMode right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskClientType left, TaskClientType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpAppsSetHostContextDetailsDisplayMode left, McpAppsSetHostContextDetailsDisplayMode right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskClientType left, TaskClientType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpAppsSetHostContextDetailsDisplayMode other && Equals(other); + public override bool Equals(object? obj) => obj is TaskClientType other && Equals(other); /// - public bool Equals(McpAppsSetHostContextDetailsDisplayMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(TaskClientType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34890,65 +37683,62 @@ public McpAppsSetHostContextDetailsDisplayMode(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 McpAppsSetHostContextDetailsDisplayMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TaskClientType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpAppsSetHostContextDetailsDisplayMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TaskClientType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsSetHostContextDetailsDisplayMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientType)); } } } -/// Platform type for responsive design. +/// Active status a client owner may publish with a progress update. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpAppsSetHostContextDetailsPlatform : IEquatable +public readonly struct TaskClientActiveStatus : 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 McpAppsSetHostContextDetailsPlatform(string value) + public TaskClientActiveStatus(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; - /// Host runs in a web browser. - public static McpAppsSetHostContextDetailsPlatform Web { get; } = new("web"); - - /// Host runs as a desktop application. - public static McpAppsSetHostContextDetailsPlatform Desktop { get; } = new("desktop"); + /// The external owner is actively working. + public static TaskClientActiveStatus Running { get; } = new("running"); - /// Host runs on a mobile device. - public static McpAppsSetHostContextDetailsPlatform Mobile { get; } = new("mobile"); + /// The external owner is connected but waiting. + public static TaskClientActiveStatus Idle { get; } = new("idle"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpAppsSetHostContextDetailsPlatform left, McpAppsSetHostContextDetailsPlatform right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(TaskClientActiveStatus left, TaskClientActiveStatus right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpAppsSetHostContextDetailsPlatform left, McpAppsSetHostContextDetailsPlatform right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(TaskClientActiveStatus left, TaskClientActiveStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpAppsSetHostContextDetailsPlatform other && Equals(other); + public override bool Equals(object? obj) => obj is TaskClientActiveStatus other && Equals(other); /// - public bool Equals(McpAppsSetHostContextDetailsPlatform other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(TaskClientActiveStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -34956,62 +37746,62 @@ public McpAppsSetHostContextDetailsPlatform(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 McpAppsSetHostContextDetailsPlatform Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TaskClientActiveStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpAppsSetHostContextDetailsPlatform value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, TaskClientActiveStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsSetHostContextDetailsPlatform)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskClientActiveStatus)); } } } -/// UI theme preference per SEP-1865. +/// Consumer allowed to call an MCP tool. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpAppsSetHostContextDetailsTheme : IEquatable +public readonly struct McpToolUiVisibility : 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 McpAppsSetHostContextDetailsTheme(string value) + public McpToolUiVisibility(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; - /// Light UI theme. - public static McpAppsSetHostContextDetailsTheme Light { get; } = new("light"); + /// The model may call the tool. + public static McpToolUiVisibility Model { get; } = new("model"); - /// Dark UI theme. - public static McpAppsSetHostContextDetailsTheme Dark { get; } = new("dark"); + /// An MCP App view may call the tool. + public static McpToolUiVisibility App { get; } = new("app"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpAppsSetHostContextDetailsTheme left, McpAppsSetHostContextDetailsTheme right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpToolUiVisibility left, McpToolUiVisibility right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpAppsSetHostContextDetailsTheme left, McpAppsSetHostContextDetailsTheme right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpToolUiVisibility left, McpToolUiVisibility right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpAppsSetHostContextDetailsTheme other && Equals(other); + public override bool Equals(object? obj) => obj is McpToolUiVisibility other && Equals(other); /// - public bool Equals(McpAppsSetHostContextDetailsTheme other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpToolUiVisibility other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35019,65 +37809,65 @@ public McpAppsSetHostContextDetailsTheme(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 McpAppsSetHostContextDetailsTheme Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpToolUiVisibility Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpAppsSetHostContextDetailsTheme value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpToolUiVisibility value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsSetHostContextDetailsTheme)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpToolUiVisibility)); } } } -/// Allowed values for the `McpAppsHostContextDetailsAvailableDisplayMode` enumeration. +/// 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpAppsHostContextDetailsAvailableDisplayMode : IEquatable +public readonly struct McpSamplingExecutionAction : 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 McpAppsHostContextDetailsAvailableDisplayMode(string value) + public McpSamplingExecutionAction(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; - /// Rendered inline within the host conversation surface. - public static McpAppsHostContextDetailsAvailableDisplayMode Inline { get; } = new("inline"); + /// The sampling inference completed and produced a result. + public static McpSamplingExecutionAction Success { get; } = new("success"); - /// Rendered as a fullscreen overlay. - public static McpAppsHostContextDetailsAvailableDisplayMode Fullscreen { get; } = new("fullscreen"); + /// The sampling inference failed or was rejected. + public static McpSamplingExecutionAction Failure { get; } = new("failure"); - /// Rendered as a picture-in-picture floating panel. - public static McpAppsHostContextDetailsAvailableDisplayMode Pip { get; } = new("pip"); + /// The sampling inference was cancelled before completion. + public static McpSamplingExecutionAction Cancelled { get; } = new("cancelled"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpAppsHostContextDetailsAvailableDisplayMode left, McpAppsHostContextDetailsAvailableDisplayMode right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpSamplingExecutionAction left, McpSamplingExecutionAction right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpAppsHostContextDetailsAvailableDisplayMode left, McpAppsHostContextDetailsAvailableDisplayMode right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpSamplingExecutionAction left, McpSamplingExecutionAction right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpAppsHostContextDetailsAvailableDisplayMode other && Equals(other); + public override bool Equals(object? obj) => obj is McpSamplingExecutionAction other && Equals(other); /// - public bool Equals(McpAppsHostContextDetailsAvailableDisplayMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpSamplingExecutionAction other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35085,65 +37875,62 @@ public McpAppsHostContextDetailsAvailableDisplayMode(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 McpAppsHostContextDetailsAvailableDisplayMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpSamplingExecutionAction Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpAppsHostContextDetailsAvailableDisplayMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpSamplingExecutionAction value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsHostContextDetailsAvailableDisplayMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpSamplingExecutionAction)); } } } -/// Current display mode (SEP-1865). +/// 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". [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpAppsHostContextDetailsDisplayMode : IEquatable +public readonly struct McpSetEnvValueModeDetails : 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 McpAppsHostContextDetailsDisplayMode(string value) + public McpSetEnvValueModeDetails(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; - /// Rendered inline within the host conversation surface. - public static McpAppsHostContextDetailsDisplayMode Inline { get; } = new("inline"); - - /// Rendered as a fullscreen overlay. - public static McpAppsHostContextDetailsDisplayMode Fullscreen { get; } = new("fullscreen"); + /// Treat MCP server environment values as literal strings. + public static McpSetEnvValueModeDetails Direct { get; } = new("direct"); - /// Rendered as a picture-in-picture floating panel. - public static McpAppsHostContextDetailsDisplayMode Pip { get; } = new("pip"); + /// Treat MCP server environment values as host-side references to resolve before launch. + public static McpSetEnvValueModeDetails Indirect { get; } = new("indirect"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpAppsHostContextDetailsDisplayMode left, McpAppsHostContextDetailsDisplayMode right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpSetEnvValueModeDetails left, McpSetEnvValueModeDetails right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpAppsHostContextDetailsDisplayMode left, McpAppsHostContextDetailsDisplayMode right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpSetEnvValueModeDetails left, McpSetEnvValueModeDetails right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpAppsHostContextDetailsDisplayMode other && Equals(other); + public override bool Equals(object? obj) => obj is McpSetEnvValueModeDetails other && Equals(other); /// - public bool Equals(McpAppsHostContextDetailsDisplayMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpSetEnvValueModeDetails other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35151,65 +37938,62 @@ public McpAppsHostContextDetailsDisplayMode(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 McpAppsHostContextDetailsDisplayMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpSetEnvValueModeDetails Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpAppsHostContextDetailsDisplayMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpSetEnvValueModeDetails value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsHostContextDetailsDisplayMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpSetEnvValueModeDetails)); } } } -/// Platform type for responsive design. +/// Outcome of starting the original prepared owned login. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpAppsHostContextDetailsPlatform : IEquatable +public readonly struct McpOwnedOauthLoginStatus : 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 McpAppsHostContextDetailsPlatform(string value) + public McpOwnedOauthLoginStatus(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; - /// Host runs in a web browser. - public static McpAppsHostContextDetailsPlatform Web { get; } = new("web"); - - /// Host runs as a desktop application. - public static McpAppsHostContextDetailsPlatform Desktop { get; } = new("desktop"); + /// The original requester may open the returned authorisation URL. + public static McpOwnedOauthLoginStatus AwaitingBrowser { get; } = new("awaiting-browser"); - /// Host runs on a mobile device. - public static McpAppsHostContextDetailsPlatform Mobile { get; } = new("mobile"); + /// Cached credentials were accepted and the original server finished reconnecting. + public static McpOwnedOauthLoginStatus Connected { get; } = new("connected"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpAppsHostContextDetailsPlatform left, McpAppsHostContextDetailsPlatform right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpOwnedOauthLoginStatus left, McpOwnedOauthLoginStatus right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpAppsHostContextDetailsPlatform left, McpAppsHostContextDetailsPlatform right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpOwnedOauthLoginStatus left, McpOwnedOauthLoginStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpAppsHostContextDetailsPlatform other && Equals(other); + public override bool Equals(object? obj) => obj is McpOwnedOauthLoginStatus other && Equals(other); /// - public bool Equals(McpAppsHostContextDetailsPlatform other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpOwnedOauthLoginStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35217,62 +38001,62 @@ public McpAppsHostContextDetailsPlatform(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 McpAppsHostContextDetailsPlatform Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpOwnedOauthLoginStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpAppsHostContextDetailsPlatform value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpOwnedOauthLoginStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsHostContextDetailsPlatform)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpOwnedOauthLoginStatus)); } } } -/// UI theme preference per SEP-1865. +/// OAuth grant type override for this login. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpAppsHostContextDetailsTheme : IEquatable +public readonly struct McpOauthLoginGrantType : 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 McpAppsHostContextDetailsTheme(string value) + public McpOauthLoginGrantType(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; - /// Light UI theme. - public static McpAppsHostContextDetailsTheme Light { get; } = new("light"); + /// Interactive browser-based OAuth flow using an authorization code, typically with PKCE. + public static McpOauthLoginGrantType AuthorizationCode { get; } = new("authorization_code"); - /// Dark UI theme. - public static McpAppsHostContextDetailsTheme Dark { get; } = new("dark"); + /// Headless OAuth flow where a confidential client authenticates directly with a client secret. + public static McpOauthLoginGrantType ClientCredentials { get; } = new("client_credentials"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpAppsHostContextDetailsTheme left, McpAppsHostContextDetailsTheme right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpOauthLoginGrantType left, McpOauthLoginGrantType right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpAppsHostContextDetailsTheme left, McpAppsHostContextDetailsTheme right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpOauthLoginGrantType left, McpOauthLoginGrantType right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpAppsHostContextDetailsTheme other && Equals(other); + public override bool Equals(object? obj) => obj is McpOauthLoginGrantType other && Equals(other); /// - public bool Equals(McpAppsHostContextDetailsTheme other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpOauthLoginGrantType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35280,74 +38064,65 @@ public McpAppsHostContextDetailsTheme(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 McpAppsHostContextDetailsTheme Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpOauthLoginGrantType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpAppsHostContextDetailsTheme value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpOauthLoginGrantType value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsHostContextDetailsTheme)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpOauthLoginGrantType)); } } } -/// Session-scoped diagnostic threshold. Capture is disabled by default and is never persisted with the session. +/// Why a passive MCP OAuth probe determined authentication is needed. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct DiagnosticLogLevel : IEquatable +public readonly struct McpOauthProbeNeedsAuthReason : 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 DiagnosticLogLevel(string value) + public McpOauthProbeNeedsAuthReason(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 capture and clear retained diagnostics. - public static DiagnosticLogLevel Off { get; } = new("off"); - - /// Capture failures only. - public static DiagnosticLogLevel Error { get; } = new("error"); - - /// Capture failures, warnings, and stderr. - public static DiagnosticLogLevel Warning { get; } = new("warning"); - - /// Capture lifecycle diagnostics. - public static DiagnosticLogLevel Info { get; } = new("info"); + /// No token was sent and the server requires authentication. + public static McpOauthProbeNeedsAuthReason Initial { get; } = new("initial"); - /// Capture protocol frames and launch diagnostics. - public static DiagnosticLogLevel Debug { get; } = new("debug"); + /// A cached token was sent and rejected. + public static McpOauthProbeNeedsAuthReason Refresh { get; } = new("refresh"); - /// Capture HTTP metadata in addition to debug diagnostics. - public static DiagnosticLogLevel Trace { get; } = new("trace"); + /// The server returned a 403 insufficient_scope challenge, indicating additional scopes or audience are needed. + public static McpOauthProbeNeedsAuthReason Upscope { get; } = new("upscope"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(DiagnosticLogLevel left, DiagnosticLogLevel right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpOauthProbeNeedsAuthReason left, McpOauthProbeNeedsAuthReason right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(DiagnosticLogLevel left, DiagnosticLogLevel right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpOauthProbeNeedsAuthReason left, McpOauthProbeNeedsAuthReason right) => !(left == right); /// - public override bool Equals(object? obj) => obj is DiagnosticLogLevel other && Equals(other); + public override bool Equals(object? obj) => obj is McpOauthProbeNeedsAuthReason other && Equals(other); /// - public bool Equals(DiagnosticLogLevel other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpOauthProbeNeedsAuthReason other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35355,62 +38130,65 @@ public DiagnosticLogLevel(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 DiagnosticLogLevel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpOauthProbeNeedsAuthReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, DiagnosticLogLevel value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpOauthProbeNeedsAuthReason value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiagnosticLogLevel)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpOauthProbeNeedsAuthReason)); } } } -/// Whether the supplied diagnostic cursor remained within the retained buffer window. +/// Allowed values for the `McpAppsSetHostContextDetailsAvailableDisplayMode` enumeration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct DiagnosticCursorStatus : IEquatable +public readonly struct McpAppsSetHostContextDetailsAvailableDisplayMode : 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 DiagnosticCursorStatus(string value) + public McpAppsSetHostContextDetailsAvailableDisplayMode(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 cursor is valid for the current retained window. - public static DiagnosticCursorStatus Ok { get; } = new("ok"); + /// Rendered inline within the host conversation surface. + public static McpAppsSetHostContextDetailsAvailableDisplayMode Inline { get; } = new("inline"); - /// The cursor no longer addresses retained records; reading resumes at the oldest retained record. - public static DiagnosticCursorStatus Expired { get; } = new("expired"); + /// Rendered as a fullscreen overlay. + public static McpAppsSetHostContextDetailsAvailableDisplayMode Fullscreen { get; } = new("fullscreen"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(DiagnosticCursorStatus left, DiagnosticCursorStatus right) => left.Equals(right); + /// Rendered as a picture-in-picture floating panel. + public static McpAppsSetHostContextDetailsAvailableDisplayMode Pip { get; } = new("pip"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(DiagnosticCursorStatus left, DiagnosticCursorStatus right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpAppsSetHostContextDetailsAvailableDisplayMode left, McpAppsSetHostContextDetailsAvailableDisplayMode right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpAppsSetHostContextDetailsAvailableDisplayMode left, McpAppsSetHostContextDetailsAvailableDisplayMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is DiagnosticCursorStatus other && Equals(other); + public override bool Equals(object? obj) => obj is McpAppsSetHostContextDetailsAvailableDisplayMode other && Equals(other); /// - public bool Equals(DiagnosticCursorStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpAppsSetHostContextDetailsAvailableDisplayMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35418,62 +38196,65 @@ public DiagnosticCursorStatus(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 DiagnosticCursorStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpAppsSetHostContextDetailsAvailableDisplayMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, DiagnosticCursorStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpAppsSetHostContextDetailsAvailableDisplayMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiagnosticCursorStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsSetHostContextDetailsAvailableDisplayMode)); } } } -/// Direction of an observed MCP protocol frame. +/// Current display mode (SEP-1865). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpDiagnosticDirection : IEquatable +public readonly struct McpAppsSetHostContextDetailsDisplayMode : 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 McpDiagnosticDirection(string value) + public McpAppsSetHostContextDetailsDisplayMode(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; - /// Frame emitted by the Copilot MCP client. - public static McpDiagnosticDirection ClientToServer { get; } = new("client-to-server"); + /// Rendered inline within the host conversation surface. + public static McpAppsSetHostContextDetailsDisplayMode Inline { get; } = new("inline"); - /// Frame received from the MCP server. - public static McpDiagnosticDirection ServerToClient { get; } = new("server-to-client"); + /// Rendered as a fullscreen overlay. + public static McpAppsSetHostContextDetailsDisplayMode Fullscreen { get; } = new("fullscreen"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpDiagnosticDirection left, McpDiagnosticDirection right) => left.Equals(right); + /// Rendered as a picture-in-picture floating panel. + public static McpAppsSetHostContextDetailsDisplayMode Pip { get; } = new("pip"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpDiagnosticDirection left, McpDiagnosticDirection right) => !(left == right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpAppsSetHostContextDetailsDisplayMode left, McpAppsSetHostContextDetailsDisplayMode right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpAppsSetHostContextDetailsDisplayMode left, McpAppsSetHostContextDetailsDisplayMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpDiagnosticDirection other && Equals(other); + public override bool Equals(object? obj) => obj is McpAppsSetHostContextDetailsDisplayMode other && Equals(other); /// - public bool Equals(McpDiagnosticDirection other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpAppsSetHostContextDetailsDisplayMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35481,68 +38262,65 @@ public McpDiagnosticDirection(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 McpDiagnosticDirection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpAppsSetHostContextDetailsDisplayMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpDiagnosticDirection value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpAppsSetHostContextDetailsDisplayMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpDiagnosticDirection)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsSetHostContextDetailsDisplayMode)); } } } -/// Category for an MCP diagnostic record. +/// Platform type for responsive design. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct McpDiagnosticKind : IEquatable +public readonly struct McpAppsSetHostContextDetailsPlatform : 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 McpDiagnosticKind(string value) + public McpAppsSetHostContextDetailsPlatform(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; - /// Connection lifecycle transition or failure. - public static McpDiagnosticKind Lifecycle { get; } = new("lifecycle"); - - /// JSON-RPC protocol frame. - public static McpDiagnosticKind Protocol { get; } = new("protocol"); + /// Host runs in a web browser. + public static McpAppsSetHostContextDetailsPlatform Web { get; } = new("web"); - /// HTTP request or response metadata. - public static McpDiagnosticKind Http { get; } = new("http"); + /// Host runs as a desktop application. + public static McpAppsSetHostContextDetailsPlatform Desktop { get; } = new("desktop"); - /// Local MCP server standard-error output. - public static McpDiagnosticKind Stderr { get; } = new("stderr"); + /// Host runs on a mobile device. + public static McpAppsSetHostContextDetailsPlatform Mobile { get; } = new("mobile"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(McpDiagnosticKind left, McpDiagnosticKind right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpAppsSetHostContextDetailsPlatform left, McpAppsSetHostContextDetailsPlatform right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(McpDiagnosticKind left, McpDiagnosticKind right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpAppsSetHostContextDetailsPlatform left, McpAppsSetHostContextDetailsPlatform right) => !(left == right); /// - public override bool Equals(object? obj) => obj is McpDiagnosticKind other && Equals(other); + public override bool Equals(object? obj) => obj is McpAppsSetHostContextDetailsPlatform other && Equals(other); /// - public bool Equals(McpDiagnosticKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpAppsSetHostContextDetailsPlatform other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35550,71 +38328,62 @@ public McpDiagnosticKind(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 McpDiagnosticKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpAppsSetHostContextDetailsPlatform Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, McpDiagnosticKind value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpAppsSetHostContextDetailsPlatform value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpDiagnosticKind)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsSetHostContextDetailsPlatform)); } } } -/// Severity of an emitted diagnostic record. +/// UI theme preference per SEP-1865. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct DiagnosticSeverity : IEquatable +public readonly struct McpAppsSetHostContextDetailsTheme : 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 DiagnosticSeverity(string value) + public McpAppsSetHostContextDetailsTheme(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; - /// Failure that prevented or interrupted communication. - public static DiagnosticSeverity Error { get; } = new("error"); - - /// A recoverable warning or server standard-error output. - public static DiagnosticSeverity Warning { get; } = new("warning"); - - /// Lifecycle transition. - public static DiagnosticSeverity Info { get; } = new("info"); - - /// Protocol-frame or launch diagnostic. - public static DiagnosticSeverity Debug { get; } = new("debug"); + /// Light UI theme. + public static McpAppsSetHostContextDetailsTheme Light { get; } = new("light"); - /// HTTP metadata diagnostic. - public static DiagnosticSeverity Trace { get; } = new("trace"); + /// Dark UI theme. + public static McpAppsSetHostContextDetailsTheme Dark { get; } = new("dark"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(DiagnosticSeverity left, DiagnosticSeverity right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpAppsSetHostContextDetailsTheme left, McpAppsSetHostContextDetailsTheme right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(DiagnosticSeverity left, DiagnosticSeverity right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpAppsSetHostContextDetailsTheme left, McpAppsSetHostContextDetailsTheme right) => !(left == right); /// - public override bool Equals(object? obj) => obj is DiagnosticSeverity other && Equals(other); + public override bool Equals(object? obj) => obj is McpAppsSetHostContextDetailsTheme other && Equals(other); /// - public bool Equals(DiagnosticSeverity other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpAppsSetHostContextDetailsTheme other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35622,59 +38391,65 @@ public DiagnosticSeverity(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 DiagnosticSeverity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpAppsSetHostContextDetailsTheme Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, DiagnosticSeverity value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpAppsSetHostContextDetailsTheme value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiagnosticSeverity)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsSetHostContextDetailsTheme)); } } } -/// A supported diagnostic source. +/// Allowed values for the `McpAppsHostContextDetailsAvailableDisplayMode` enumeration. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct DiagnosticSource : IEquatable +public readonly struct McpAppsHostContextDetailsAvailableDisplayMode : 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 DiagnosticSource(string value) + public McpAppsHostContextDetailsAvailableDisplayMode(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; - /// Gets the mcp value. - public static DiagnosticSource Mcp { get; } = new("mcp"); + /// Rendered inline within the host conversation surface. + public static McpAppsHostContextDetailsAvailableDisplayMode Inline { get; } = new("inline"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(DiagnosticSource left, DiagnosticSource right) => left.Equals(right); + /// Rendered as a fullscreen overlay. + public static McpAppsHostContextDetailsAvailableDisplayMode Fullscreen { get; } = new("fullscreen"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(DiagnosticSource left, DiagnosticSource right) => !(left == right); + /// Rendered as a picture-in-picture floating panel. + public static McpAppsHostContextDetailsAvailableDisplayMode Pip { get; } = new("pip"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpAppsHostContextDetailsAvailableDisplayMode left, McpAppsHostContextDetailsAvailableDisplayMode right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpAppsHostContextDetailsAvailableDisplayMode left, McpAppsHostContextDetailsAvailableDisplayMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is DiagnosticSource other && Equals(other); + public override bool Equals(object? obj) => obj is McpAppsHostContextDetailsAvailableDisplayMode other && Equals(other); /// - public bool Equals(DiagnosticSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpAppsHostContextDetailsAvailableDisplayMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35682,65 +38457,65 @@ public DiagnosticSource(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 DiagnosticSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpAppsHostContextDetailsAvailableDisplayMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, DiagnosticSource value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpAppsHostContextDetailsAvailableDisplayMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiagnosticSource)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsHostContextDetailsAvailableDisplayMode)); } } } -/// Availability of the EXPERIMENTAL session connector API. +/// Current display mode (SEP-1865). [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ConnectorAvailability : IEquatable +public readonly struct McpAppsHostContextDetailsDisplayMode : 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 ConnectorAvailability(string value) + public McpAppsHostContextDetailsDisplayMode(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 resolved Connector feature is enabled and Connector requests are permitted. - public static ConnectorAvailability Enabled { get; } = new("enabled"); + /// Rendered inline within the host conversation surface. + public static McpAppsHostContextDetailsDisplayMode Inline { get; } = new("inline"); - /// The resolved Connector feature is off. No Connector service request is made while disabled. - public static ConnectorAvailability Disabled { get; } = new("disabled"); + /// Rendered as a fullscreen overlay. + public static McpAppsHostContextDetailsDisplayMode Fullscreen { get; } = new("fullscreen"); - /// The session has no eligible host-owned GitHub account or does not support local Connector projection. - public static ConnectorAvailability Unavailable { get; } = new("unavailable"); + /// Rendered as a picture-in-picture floating panel. + public static McpAppsHostContextDetailsDisplayMode Pip { get; } = new("pip"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ConnectorAvailability left, ConnectorAvailability right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpAppsHostContextDetailsDisplayMode left, McpAppsHostContextDetailsDisplayMode right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ConnectorAvailability left, ConnectorAvailability right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpAppsHostContextDetailsDisplayMode left, McpAppsHostContextDetailsDisplayMode right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ConnectorAvailability other && Equals(other); + public override bool Equals(object? obj) => obj is McpAppsHostContextDetailsDisplayMode other && Equals(other); /// - public bool Equals(ConnectorAvailability other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpAppsHostContextDetailsDisplayMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35748,59 +38523,65 @@ public ConnectorAvailability(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 ConnectorAvailability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpAppsHostContextDetailsDisplayMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ConnectorAvailability value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpAppsHostContextDetailsDisplayMode value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectorAvailability)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsHostContextDetailsDisplayMode)); } } } -/// Stable OAuth scope whose absence prevents Connector management. +/// Platform type for responsive design. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ConnectorAuthorizationScope : IEquatable +public readonly struct McpAppsHostContextDetailsPlatform : 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 ConnectorAuthorizationScope(string value) + public McpAppsHostContextDetailsPlatform(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; - /// Allows Copilot to manage Connector connections and use their tools. - public static ConnectorAuthorizationScope WritePluginGatewayConnections { get; } = new("write_plugin_gateway_connections"); + /// Host runs in a web browser. + public static McpAppsHostContextDetailsPlatform Web { get; } = new("web"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ConnectorAuthorizationScope left, ConnectorAuthorizationScope right) => left.Equals(right); + /// Host runs as a desktop application. + public static McpAppsHostContextDetailsPlatform Desktop { get; } = new("desktop"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ConnectorAuthorizationScope left, ConnectorAuthorizationScope right) => !(left == right); + /// Host runs on a mobile device. + public static McpAppsHostContextDetailsPlatform Mobile { get; } = new("mobile"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpAppsHostContextDetailsPlatform left, McpAppsHostContextDetailsPlatform right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpAppsHostContextDetailsPlatform left, McpAppsHostContextDetailsPlatform right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ConnectorAuthorizationScope other && Equals(other); + public override bool Equals(object? obj) => obj is McpAppsHostContextDetailsPlatform other && Equals(other); /// - public bool Equals(ConnectorAuthorizationScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpAppsHostContextDetailsPlatform other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35808,71 +38589,62 @@ public ConnectorAuthorizationScope(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 ConnectorAuthorizationScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpAppsHostContextDetailsPlatform Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ConnectorAuthorizationScope value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpAppsHostContextDetailsPlatform value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectorAuthorizationScope)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsHostContextDetailsPlatform)); } } } -/// Authoritative service connection state for one Connector. +/// UI theme preference per SEP-1865. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ConnectorCatalogStatus : IEquatable +public readonly struct McpAppsHostContextDetailsTheme : 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 ConnectorCatalogStatus(string value) + public McpAppsHostContextDetailsTheme(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 Connector is available but not connected. - public static ConnectorCatalogStatus NotConnected { get; } = new("not_connected"); - - /// The Connector service is still completing connection or consent. - public static ConnectorCatalogStatus Pending { get; } = new("pending"); - - /// The Connector is connected and may contribute MCP servers. - public static ConnectorCatalogStatus Connected { get; } = new("connected"); - - /// The Connector service reports an unusable connection. - public static ConnectorCatalogStatus Error { get; } = new("error"); + /// Light UI theme. + public static McpAppsHostContextDetailsTheme Light { get; } = new("light"); - /// The service returned a future or unrecognized state. - public static ConnectorCatalogStatus Unknown { get; } = new("unknown"); + /// Dark UI theme. + public static McpAppsHostContextDetailsTheme Dark { get; } = new("dark"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ConnectorCatalogStatus left, ConnectorCatalogStatus right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpAppsHostContextDetailsTheme left, McpAppsHostContextDetailsTheme right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ConnectorCatalogStatus left, ConnectorCatalogStatus right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpAppsHostContextDetailsTheme left, McpAppsHostContextDetailsTheme right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ConnectorCatalogStatus other && Equals(other); + public override bool Equals(object? obj) => obj is McpAppsHostContextDetailsTheme other && Equals(other); /// - public bool Equals(ConnectorCatalogStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpAppsHostContextDetailsTheme other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35880,77 +38652,62 @@ public ConnectorCatalogStatus(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 ConnectorCatalogStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpAppsHostContextDetailsTheme Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ConnectorCatalogStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpAppsHostContextDetailsTheme value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectorCatalogStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpAppsHostContextDetailsTheme)); } } } -/// Live MCP status of one Connector-owned runtime server. +/// The sender role of an MCP prompt message. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ConnectorMcpStatus : IEquatable +public readonly struct McpPromptRole : 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 ConnectorMcpStatus(string value) + public McpPromptRole(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 server is connected and its tools are available. - public static ConnectorMcpStatus Connected { get; } = new("connected"); - - /// The server connection is still being established. - public static ConnectorMcpStatus Pending { get; } = new("pending"); - - /// The server requires refreshed GitHub authorization. - public static ConnectorMcpStatus NeedsAuth { get; } = new("needs_auth"); - - /// The server failed to connect or initialize. - public static ConnectorMcpStatus Failed { get; } = new("failed"); - - /// The server is intentionally stopped, including when managed policy blocks it. - public static ConnectorMcpStatus Stopped { get; } = new("stopped"); - - /// The server is configured but explicitly disabled. - public static ConnectorMcpStatus Disabled { get; } = new("disabled"); + /// A message from the user. + public static McpPromptRole User { get; } = new("user"); - /// The Connector currently has no live server configuration. - public static ConnectorMcpStatus NotConfigured { get; } = new("not_configured"); + /// A message from the assistant. + public static McpPromptRole Assistant { get; } = new("assistant"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ConnectorMcpStatus left, ConnectorMcpStatus right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpPromptRole left, McpPromptRole right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ConnectorMcpStatus left, ConnectorMcpStatus right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpPromptRole left, McpPromptRole right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ConnectorMcpStatus other && Equals(other); + public override bool Equals(object? obj) => obj is McpPromptRole other && Equals(other); /// - public bool Equals(ConnectorMcpStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpPromptRole other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -35958,62 +38715,74 @@ public ConnectorMcpStatus(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 ConnectorMcpStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpPromptRole Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ConnectorMcpStatus value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpPromptRole value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectorMcpStatus)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpPromptRole)); } } } -/// Transport to be used for provider requests. +/// Session-scoped diagnostic threshold. Capture is disabled by default and is never persisted with the session. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProviderEndpointTransport : IEquatable +public readonly struct DiagnosticLogLevel : 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 ProviderEndpointTransport(string value) + public DiagnosticLogLevel(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; - /// HTTP request/streaming transport. - public static ProviderEndpointTransport Http { get; } = new("http"); + /// Disable capture and clear retained diagnostics. + public static DiagnosticLogLevel Off { get; } = new("off"); - /// WebSocket transport. - public static ProviderEndpointTransport Websockets { get; } = new("websockets"); + /// Capture failures only. + public static DiagnosticLogLevel Error { get; } = new("error"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProviderEndpointTransport left, ProviderEndpointTransport right) => left.Equals(right); + /// Capture failures, warnings, and stderr. + public static DiagnosticLogLevel Warning { get; } = new("warning"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProviderEndpointTransport left, ProviderEndpointTransport right) => !(left == right); + /// Capture lifecycle diagnostics. + public static DiagnosticLogLevel Info { get; } = new("info"); + + /// Capture protocol frames and launch diagnostics. + public static DiagnosticLogLevel Debug { get; } = new("debug"); + + /// Capture HTTP metadata in addition to debug diagnostics. + public static DiagnosticLogLevel Trace { get; } = new("trace"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(DiagnosticLogLevel left, DiagnosticLogLevel right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(DiagnosticLogLevel left, DiagnosticLogLevel right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProviderEndpointTransport other && Equals(other); + public override bool Equals(object? obj) => obj is DiagnosticLogLevel other && Equals(other); /// - public bool Equals(ProviderEndpointTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(DiagnosticLogLevel other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36021,65 +38790,62 @@ public ProviderEndpointTransport(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 ProviderEndpointTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override DiagnosticLogLevel Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProviderEndpointTransport value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, DiagnosticLogLevel value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderEndpointTransport)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiagnosticLogLevel)); } } } -/// Provider family. Matches the `type` field of a BYOK provider config. +/// Whether the supplied diagnostic cursor remained within the retained buffer window. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProviderEndpointType : IEquatable +public readonly struct DiagnosticCursorStatus : 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 ProviderEndpointType(string value) + public DiagnosticCursorStatus(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; - /// OpenAI-compatible endpoint (use the OpenAI client library). - public static ProviderEndpointType Openai { get; } = new("openai"); - - /// Azure OpenAI endpoint (use the OpenAI client library with the Azure base URL). - public static ProviderEndpointType Azure { get; } = new("azure"); + /// The cursor is valid for the current retained window. + public static DiagnosticCursorStatus Ok { get; } = new("ok"); - /// Anthropic endpoint (use the Anthropic client library). - public static ProviderEndpointType Anthropic { get; } = new("anthropic"); + /// The cursor no longer addresses retained records; reading resumes at the oldest retained record. + public static DiagnosticCursorStatus Expired { get; } = new("expired"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProviderEndpointType left, ProviderEndpointType right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(DiagnosticCursorStatus left, DiagnosticCursorStatus right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProviderEndpointType left, ProviderEndpointType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(DiagnosticCursorStatus left, DiagnosticCursorStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProviderEndpointType other && Equals(other); + public override bool Equals(object? obj) => obj is DiagnosticCursorStatus other && Equals(other); /// - public bool Equals(ProviderEndpointType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(DiagnosticCursorStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36087,62 +38853,62 @@ public ProviderEndpointType(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 ProviderEndpointType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override DiagnosticCursorStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProviderEndpointType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, DiagnosticCursorStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderEndpointType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiagnosticCursorStatus)); } } } -/// Wire API to be used, when required for the provider type. +/// Direction of an observed MCP protocol frame. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProviderEndpointWireApi : IEquatable +public readonly struct McpDiagnosticDirection : 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 ProviderEndpointWireApi(string value) + public McpDiagnosticDirection(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; - /// Classic chat-completions request shape. - public static ProviderEndpointWireApi Completions { get; } = new("completions"); + /// Frame emitted by the Copilot MCP client. + public static McpDiagnosticDirection ClientToServer { get; } = new("client-to-server"); - /// Newer responses request shape. - public static ProviderEndpointWireApi Responses { get; } = new("responses"); + /// Frame received from the MCP server. + public static McpDiagnosticDirection ServerToClient { get; } = new("server-to-client"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProviderEndpointWireApi left, ProviderEndpointWireApi right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpDiagnosticDirection left, McpDiagnosticDirection right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProviderEndpointWireApi left, ProviderEndpointWireApi right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpDiagnosticDirection left, McpDiagnosticDirection right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProviderEndpointWireApi other && Equals(other); + public override bool Equals(object? obj) => obj is McpDiagnosticDirection other && Equals(other); /// - public bool Equals(ProviderEndpointWireApi other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpDiagnosticDirection other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36150,59 +38916,68 @@ public ProviderEndpointWireApi(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 ProviderEndpointWireApi Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpDiagnosticDirection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProviderEndpointWireApi value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpDiagnosticDirection value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderEndpointWireApi)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpDiagnosticDirection)); } } } -/// Defines the allowed values. +/// Category for an MCP diagnostic record. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProtocolAppendMode : IEquatable +public readonly struct McpDiagnosticKind : 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 ProtocolAppendMode(string value) + public McpDiagnosticKind(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; - /// Gets the append value. - public static ProtocolAppendMode Append { get; } = new("append"); + /// Connection lifecycle transition or failure. + public static McpDiagnosticKind Lifecycle { get; } = new("lifecycle"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProtocolAppendMode left, ProtocolAppendMode right) => left.Equals(right); + /// JSON-RPC protocol frame. + public static McpDiagnosticKind Protocol { get; } = new("protocol"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProtocolAppendMode left, ProtocolAppendMode right) => !(left == right); + /// HTTP request or response metadata. + public static McpDiagnosticKind Http { get; } = new("http"); + + /// Local MCP server standard-error output. + public static McpDiagnosticKind Stderr { get; } = new("stderr"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(McpDiagnosticKind left, McpDiagnosticKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(McpDiagnosticKind left, McpDiagnosticKind right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProtocolAppendMode other && Equals(other); + public override bool Equals(object? obj) => obj is McpDiagnosticKind other && Equals(other); /// - public bool Equals(ProtocolAppendMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(McpDiagnosticKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36210,59 +38985,71 @@ public ProtocolAppendMode(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 ProtocolAppendMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override McpDiagnosticKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProtocolAppendMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, McpDiagnosticKind value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProtocolAppendMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(McpDiagnosticKind)); } } } -/// Defines the allowed values. +/// Severity of an emitted diagnostic record. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProtocolReplaceMode : IEquatable +public readonly struct DiagnosticSeverity : 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 ProtocolReplaceMode(string value) + public DiagnosticSeverity(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; - /// Gets the replace value. - public static ProtocolReplaceMode Replace { get; } = new("replace"); + /// Failure that prevented or interrupted communication. + public static DiagnosticSeverity Error { get; } = new("error"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProtocolReplaceMode left, ProtocolReplaceMode right) => left.Equals(right); + /// A recoverable warning or server standard-error output. + public static DiagnosticSeverity Warning { get; } = new("warning"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProtocolReplaceMode left, ProtocolReplaceMode right) => !(left == right); + /// Lifecycle transition. + public static DiagnosticSeverity Info { get; } = new("info"); + + /// Protocol-frame or launch diagnostic. + public static DiagnosticSeverity Debug { get; } = new("debug"); + + /// HTTP metadata diagnostic. + public static DiagnosticSeverity Trace { get; } = new("trace"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(DiagnosticSeverity left, DiagnosticSeverity right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(DiagnosticSeverity left, DiagnosticSeverity right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProtocolReplaceMode other && Equals(other); + public override bool Equals(object? obj) => obj is DiagnosticSeverity other && Equals(other); /// - public bool Equals(ProtocolReplaceMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(DiagnosticSeverity other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36270,59 +39057,59 @@ public ProtocolReplaceMode(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 ProtocolReplaceMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override DiagnosticSeverity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProtocolReplaceMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, DiagnosticSeverity value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProtocolReplaceMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiagnosticSeverity)); } } } -/// Defines the allowed values. +/// A supported diagnostic source. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProtocolCustomizeMode : IEquatable +public readonly struct DiagnosticSource : 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 ProtocolCustomizeMode(string value) + public DiagnosticSource(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; - /// Gets the customize value. - public static ProtocolCustomizeMode Customize { get; } = new("customize"); + /// Gets the mcp value. + public static DiagnosticSource Mcp { get; } = new("mcp"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProtocolCustomizeMode left, ProtocolCustomizeMode right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(DiagnosticSource left, DiagnosticSource right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProtocolCustomizeMode left, ProtocolCustomizeMode right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(DiagnosticSource left, DiagnosticSource right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProtocolCustomizeMode other && Equals(other); + public override bool Equals(object? obj) => obj is DiagnosticSource other && Equals(other); /// - public bool Equals(ProtocolCustomizeMode other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(DiagnosticSource other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36330,68 +39117,65 @@ public ProtocolCustomizeMode(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 ProtocolCustomizeMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override DiagnosticSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProtocolCustomizeMode value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, DiagnosticSource value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProtocolCustomizeMode)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(DiagnosticSource)); } } } -/// Defines the allowed values. +/// Availability of the EXPERIMENTAL session connector API. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProtocolStaticSectionAction : IEquatable +public readonly struct ConnectorAvailability : 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 ProtocolStaticSectionAction(string value) + public ConnectorAvailability(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; - /// Replace the section content. - public static ProtocolStaticSectionAction Replace { get; } = new("replace"); - - /// Remove the section content. - public static ProtocolStaticSectionAction Remove { get; } = new("remove"); + /// The resolved Connector feature is enabled and Connector requests are permitted. + public static ConnectorAvailability Enabled { get; } = new("enabled"); - /// Append content to the section. - public static ProtocolStaticSectionAction Append { get; } = new("append"); + /// The resolved Connector feature is off. No Connector service request is made while disabled. + public static ConnectorAvailability Disabled { get; } = new("disabled"); - /// Prepend content to the section. - public static ProtocolStaticSectionAction Prepend { get; } = new("prepend"); + /// The session has no eligible host-owned GitHub account or does not support local Connector projection. + public static ConnectorAvailability Unavailable { get; } = new("unavailable"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProtocolStaticSectionAction left, ProtocolStaticSectionAction right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ConnectorAvailability left, ConnectorAvailability right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProtocolStaticSectionAction left, ProtocolStaticSectionAction right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ConnectorAvailability left, ConnectorAvailability right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProtocolStaticSectionAction other && Equals(other); + public override bool Equals(object? obj) => obj is ConnectorAvailability other && Equals(other); /// - public bool Equals(ProtocolStaticSectionAction other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ConnectorAvailability other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36399,62 +39183,59 @@ public ProtocolStaticSectionAction(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 ProtocolStaticSectionAction Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ConnectorAvailability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProtocolStaticSectionAction value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ConnectorAvailability value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProtocolStaticSectionAction)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectorAvailability)); } } } -/// Provider transport. Defaults to "http". +/// Stable OAuth scope whose absence prevents Connector management. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProviderConfigTransport : IEquatable +public readonly struct ConnectorAuthorizationScope : 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 ProviderConfigTransport(string value) + public ConnectorAuthorizationScope(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; - /// HTTP request/streaming transport. - public static ProviderConfigTransport Http { get; } = new("http"); - - /// WebSocket transport. - public static ProviderConfigTransport Websockets { get; } = new("websockets"); + /// Allows Copilot to manage Connector connections and use their tools. + public static ConnectorAuthorizationScope WritePluginGatewayConnections { get; } = new("write_plugin_gateway_connections"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProviderConfigTransport left, ProviderConfigTransport right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ConnectorAuthorizationScope left, ConnectorAuthorizationScope right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProviderConfigTransport left, ProviderConfigTransport right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ConnectorAuthorizationScope left, ConnectorAuthorizationScope right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProviderConfigTransport other && Equals(other); + public override bool Equals(object? obj) => obj is ConnectorAuthorizationScope other && Equals(other); /// - public bool Equals(ProviderConfigTransport other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ConnectorAuthorizationScope other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36462,65 +39243,77 @@ public ProviderConfigTransport(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 ProviderConfigTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ConnectorAuthorizationScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProviderConfigTransport value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ConnectorAuthorizationScope value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderConfigTransport)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectorAuthorizationScope)); } } } -/// Provider type. Defaults to "openai" for generic OpenAI-compatible APIs. +/// Live MCP status of one Connector-owned runtime server. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProviderConfigType : IEquatable +public readonly struct ConnectorMcpStatus : 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 ProviderConfigType(string value) + public ConnectorMcpStatus(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; - /// Generic OpenAI-compatible API. - public static ProviderConfigType Openai { get; } = new("openai"); + /// The server is connected and its tools are available. + public static ConnectorMcpStatus Connected { get; } = new("connected"); - /// Azure OpenAI Service endpoint. - public static ProviderConfigType Azure { get; } = new("azure"); + /// The server connection is still being established. + public static ConnectorMcpStatus Pending { get; } = new("pending"); - /// Anthropic API endpoint. - public static ProviderConfigType Anthropic { get; } = new("anthropic"); + /// The server requires refreshed GitHub authorization. + public static ConnectorMcpStatus NeedsAuth { get; } = new("needs_auth"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProviderConfigType left, ProviderConfigType right) => left.Equals(right); + /// The server failed to connect or initialize. + public static ConnectorMcpStatus Failed { get; } = new("failed"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProviderConfigType left, ProviderConfigType right) => !(left == right); + /// The server is intentionally stopped, including when managed policy blocks it. + public static ConnectorMcpStatus Stopped { get; } = new("stopped"); + + /// The server is configured but explicitly disabled. + public static ConnectorMcpStatus Disabled { get; } = new("disabled"); + + /// The Connector currently has no live server configuration. + public static ConnectorMcpStatus NotConfigured { get; } = new("not_configured"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ConnectorMcpStatus left, ConnectorMcpStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ConnectorMcpStatus left, ConnectorMcpStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProviderConfigType other && Equals(other); + public override bool Equals(object? obj) => obj is ConnectorMcpStatus other && Equals(other); /// - public bool Equals(ProviderConfigType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ConnectorMcpStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36528,62 +39321,74 @@ public ProviderConfigType(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 ProviderConfigType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ConnectorMcpStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProviderConfigType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ConnectorMcpStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderConfigType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ConnectorMcpStatus)); } } } -/// Wire API format (openai/azure only). Defaults to "completions". +/// What retrying did for one plugin required by managed settings. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ProviderConfigWireApi : IEquatable +public readonly struct ManagedPluginRetryStatus : 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 ProviderConfigWireApi(string value) + public ManagedPluginRetryStatus(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; - /// OpenAI Chat Completions wire format. - public static ProviderConfigWireApi Completions { get; } = new("completions"); + /// The plugin was missing and is now installed. + public static ManagedPluginRetryStatus Installed { get; } = new("installed"); - /// OpenAI Responses API wire format. - public static ProviderConfigWireApi Responses { get; } = new("responses"); + /// An update was available and is now installed. + public static ManagedPluginRetryStatus Updated { get; } = new("updated"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ProviderConfigWireApi left, ProviderConfigWireApi right) => left.Equals(right); + /// The plugin was already installed and current, so nothing was done. + public static ManagedPluginRetryStatus AlreadyPresent { get; } = new("already_present"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ProviderConfigWireApi left, ProviderConfigWireApi right) => !(left == right); + /// Checking, installing, or updating the plugin failed. See `error`. + public static ManagedPluginRetryStatus Failed { get; } = new("failed"); + + /// The plugin was not prepared because another session was changing plugins or the managed settings changed during the retry. It is prepared again before a later message. + public static ManagedPluginRetryStatus Deferred { get; } = new("deferred"); + + /// The session's managed settings do not require this plugin, so nothing was done. + public static ManagedPluginRetryStatus NotRequired { get; } = new("not_required"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedPluginRetryStatus left, ManagedPluginRetryStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedPluginRetryStatus left, ManagedPluginRetryStatus right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ProviderConfigWireApi other && Equals(other); + public override bool Equals(object? obj) => obj is ManagedPluginRetryStatus other && Equals(other); /// - public bool Equals(ProviderConfigWireApi other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ManagedPluginRetryStatus other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -36591,20 +39396,20 @@ public ProviderConfigWireApi(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 ProviderConfigWireApi Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ManagedPluginRetryStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ProviderConfigWireApi value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ManagedPluginRetryStatus value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderConfigWireApi)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedPluginRetryStatus)); } } } @@ -38917,69 +41722,6 @@ public override void Write(Utf8JsonWriter writer, WorkspaceSummaryHostType value } -/// Hosting platform type of the repository. -[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -[JsonConverter(typeof(Converter))] -[DebuggerDisplay("{Value,nq}")] -public readonly struct SessionWorkingDirectoryContextHostType : IEquatable -{ - private readonly string? _value; - - /// Initializes a new instance of the struct. - /// The value to associate with this . - [JsonConstructor] - public SessionWorkingDirectoryContextHostType(string value) - { - ArgumentException.ThrowIfNullOrWhiteSpace(value); - _value = value; - } - - /// Gets the value associated with this . - public string Value => _value ?? string.Empty; - - /// The working directory repository is hosted on GitHub. - public static SessionWorkingDirectoryContextHostType GitHub { get; } = new("github"); - - /// The working directory repository is hosted on Azure DevOps. - public static SessionWorkingDirectoryContextHostType Ado { get; } = new("ado"); - - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(SessionWorkingDirectoryContextHostType left, SessionWorkingDirectoryContextHostType right) => left.Equals(right); - - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(SessionWorkingDirectoryContextHostType left, SessionWorkingDirectoryContextHostType right) => !(left == right); - - /// - public override bool Equals(object? obj) => obj is SessionWorkingDirectoryContextHostType other && Equals(other); - - /// - public bool Equals(SessionWorkingDirectoryContextHostType 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 SessionWorkingDirectoryContextHostType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); - } - - /// - public override void Write(Utf8JsonWriter writer, SessionWorkingDirectoryContextHostType value, JsonSerializerOptions options) - { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionWorkingDirectoryContextHostType)); - } - } -} - - /// Rust-owned settings predicates exposed across the SDK boundary. Raw feature-flag names are intentionally not part of the contract. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -39624,6 +42366,87 @@ public override void Write(Utf8JsonWriter writer, EventsAgentScope value, JsonSe } +/// Copilot plan tier used by the session quota projection. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct SessionQuotaPlanTier : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public SessionQuotaPlanTier(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Copilot Free. + public static SessionQuotaPlanTier Free { get; } = new("free"); + + /// Copilot education entitlement. + public static SessionQuotaPlanTier Edu { get; } = new("edu"); + + /// Copilot Pro. + public static SessionQuotaPlanTier Pro { get; } = new("pro"); + + /// Copilot Pro+. + public static SessionQuotaPlanTier ProPlus { get; } = new("pro_plus"); + + /// Copilot Business. + public static SessionQuotaPlanTier Business { get; } = new("business"); + + /// Copilot Enterprise. + public static SessionQuotaPlanTier Enterprise { get; } = new("enterprise"); + + /// Copilot Max. + public static SessionQuotaPlanTier Max { get; } = new("max"); + + /// The account's plan tier is not known. + public static SessionQuotaPlanTier Unknown { get; } = new("unknown"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(SessionQuotaPlanTier left, SessionQuotaPlanTier right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(SessionQuotaPlanTier left, SessionQuotaPlanTier right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is SessionQuotaPlanTier other && Equals(other); + + /// + public bool Equals(SessionQuotaPlanTier 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 SessionQuotaPlanTier Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, SessionQuotaPlanTier value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionQuotaPlanTier)); + } + } +} + + /// Client population used for the prediction baseline. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonConverter(typeof(Converter))] @@ -40729,6 +43552,12 @@ public async Task RegisterExtensionLaunchProviderAsync(CancellationToken cancell 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 ?? @@ -40741,6 +43570,24 @@ public async Task RegisterExtensionLaunchProviderAsync(CancellationToken cancell 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; + /// ManagedSettings APIs. public ServerManagedSettingsApi ManagedSettings => field ?? @@ -40777,8 +43624,8 @@ public async Task RegisterExtensionLaunchProviderAsync(CancellationToken cancell Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; - /// Accounts APIs. - public ServerAccountsApi Accounts => + /// Connectors APIs. + public ServerConnectorsApi Connectors => field ?? Interlocked.CompareExchange(ref field, new(_rpc), null) ?? field; @@ -40856,18 +43703,35 @@ public async Task PublishSessionAsync(string hostId, s return await CopilotClient.InvokeRpcAsync(_rpc, "host.publishSession", [request], cancellationToken); } + /// Reads the complete advertised live and dormant session catalog of an existing host owned by this connection. Does not start hosting, subscribe to sessions, or guarantee resumability. + /// + /// Listener UUID returned by host.start; the caller must own it. Omit to read + /// the saved default compute-identity catalog without starting a listener. + /// + /// The to monitor for cancellation requests. The default is . + /// The complete live and dormant advertised catalog, without subscribing to sessions. + public async Task ListSessionsAsync(string? hostId = null, CancellationToken cancellationToken = default) + { + var request = new HostListSessionsRequest { HostId = hostId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "host.listSessions", [request], cancellationToken); + } + /// Requests app-owned materialization over the owning SDK participant. /// Unique identity for this participation, independent of the session lifetime. /// Host-selected SDK creation or resume settings, without executable callbacks or tools. /// Resume an app-owned durable session instead of creating a new session. + /// + /// For an explicit publication, borrow an existing owning-connection resident + /// instead of invoking the resume factory. Cold sessions still use the factory. + /// /// The to monitor for cancellation requests. The default is . /// The resident session the application has materialized on its own connection. - internal async Task CreateSessionAsync(string handoffId, IDictionary config, bool? resume = null, CancellationToken cancellationToken = default) + internal async Task CreateSessionAsync(string handoffId, IDictionary config, bool? resume = null, bool? preferResident = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(handoffId); ArgumentNullException.ThrowIfNull(config); - var request = new HostCreateSessionRequest { HandoffId = handoffId, Config = config, Resume = resume }; + var request = new HostCreateSessionRequest { HandoffId = handoffId, Config = config, Resume = resume, PreferResident = preferResident }; return await CopilotClient.InvokeRpcAsync(_rpc, "host.createSession", [request], cancellationToken); } @@ -40899,6 +43763,17 @@ public async Task StartAsync(string hostId, HostLocalServerOpti return await CopilotClient.InvokeRpcAsync(_rpc, "host.start", [request], cancellationToken); } + /// Starts a connection-owned AHP host with explicit localServer and/or githubEnvironment transports as a supervised SDK participant. + /// Starts a supervised AHP host with at least one explicitly selected transport. + /// The to monitor for cancellation requests. The default is . + /// Listener readiness, returned only after binding and the supervised participant's SDK handshake. + public async Task StartAsync(HostStartRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(request.HostId); + return await CopilotClient.InvokeRpcAsync(_rpc, "host.start", [request], cancellationToken); + } + /// Stops a listener owned by this SDK connection and joins its cleanup without deleting sessions. /// Listener UUID. Unknown or successfully stopped IDs are harmless. /// The to monitor for cancellation requests. The default is . @@ -41012,6 +43887,72 @@ public async Task GetHostSupportAsync(CancellationToken canc { return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.getHostSupport", [], cancellationToken); } + + /// ProxyCa APIs. + public ServerSandboxProxyCaApi ProxyCa => + field ?? + Interlocked.CompareExchange(ref field, new(_rpc), null) ?? + field; +} + +/// Provides server-scoped SandboxProxyCa APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerSandboxProxyCaApi +{ + private readonly JsonRpc _rpc; + + internal ServerSandboxProxyCaApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Reports whether the persistent certificate authority of the sandbox credential proxy exists, whether OS trust includes it, and whether it must be rotated. Changes nothing. + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + /// The to monitor for cancellation requests. The default is . + /// Status of the persistent certificate authority of the sandbox credential proxy. + public async Task GetStatusAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) + { + var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.getStatus", [request], cancellationToken); + } + + /// Creates the persistent certificate authority of the sandbox credential proxy if none is stored, without changing OS trust, and returns the path of its public certificate. Keeps an existing certificate authority, even one that must be rotated. Fails where OS trust is unsupported. Trust it with sandbox.proxyCa.trust: the CLI trusts only the hosts in the saved user settings, so it refuses a certificate authority that also covers hosts from sandboxConfig. + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + /// The to monitor for cancellation requests. The default is . + /// Result of creating the persistent certificate authority of the sandbox credential proxy. + public async Task CreateAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) + { + var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.create", [request], cancellationToken); + } + + /// Replaces the persistent certificate authority of the sandbox credential proxy with a new one for the current credential hosts. If OS trust included the old one, removes it and trusts the new one, which can show an OS authentication prompt. Running sandboxed tools keep the old certificate authority until they restart. + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + /// The to monitor for cancellation requests. The default is . + /// Status of the persistent certificate authority of the sandbox credential proxy. + public async Task RotateAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) + { + var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.rotate", [request], cancellationToken); + } + + /// Adds the persistent certificate authority of the sandbox credential proxy to OS trust, so sandboxed clients that read only OS trust accept the proxy. Call create first. Refuses a certificate authority that is not constrained to the current credential hosts. Can show an OS authentication prompt. + /// The sandbox configuration that the host gives its sessions. The runtime reads the credential hosts from `auth` and `credentials`; it ignores `enabled` and the other fields. + /// The to monitor for cancellation requests. The default is . + /// Status of the persistent certificate authority of the sandbox credential proxy. + public async Task TrustAsync(SandboxConfig? sandboxConfig = null, CancellationToken cancellationToken = default) + { + var request = new SandboxProxyCaRequest { SandboxConfig = sandboxConfig }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.trust", [request], cancellationToken); + } + + /// Removes the persistent certificate authority of the sandbox credential proxy from OS trust. Keeps the stored certificate authority. Can show an OS authentication prompt. Sandboxed clients that read only OS trust then reject the proxy; clients that read the per-process certificate bundle continue to work. + /// The to monitor for cancellation requests. The default is . + /// Status of the persistent certificate authority of the sandbox credential proxy. + public async Task RemoveAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "sandbox.proxyCa.remove", [], cancellationToken); + } } /// Provides server-scoped Tools APIs. @@ -41978,6 +44919,64 @@ public async Task GetDiscoveryPathsAsync(IList? var request = new AgentsGetDiscoveryPathsRequest { ProjectPaths = projectPaths, ExcludeHostAgents = excludeHostAgents }; 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. + internal async Task GetBuiltinsAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "agents.getBuiltins", [], cancellationToken); + } + + /// Lists the shipped agents a client should offer right now, filtered by the feature flags it passes. `getBuiltins` names every agent the runtime knows about; some of those are gated, so a client rendering a picker wants this narrower list together with the description to show beside each name. + /// Feature flag values keyed by name, evaluated with the runtime's truthiness rules. Omit or pass null for no flags. + /// Flag overrides keyed by name. A null entry uses the corresponding base flag; false explicitly disables it. Omit or pass null for no overrides. + /// The surface asking, which gates agents that only apply to one client. Omit or pass null to apply no client filter. + /// The to monitor for cancellation requests. The default is . + /// The shipped agents available under the requested flags. + internal async Task GetAvailableBuiltinsAsync(IDictionary? featureFlags = null, IDictionary? overrides = null, string? context = null, CancellationToken cancellationToken = default) + { + var request = new AgentsGetAvailableBuiltinsRequest { FeatureFlags = featureFlags, Overrides = overrides, Context = context }; + return await CopilotClient.InvokeRpcAsync(_rpc, "agents.getAvailableBuiltins", [request], cancellationToken); + } + + /// Loads one shipped agent's YAML definition, for a client that needs what the agent declares rather than only its name. `getBuiltins` reports which names have a definition to load: a name outside its `yamlBasedNames` is special-cased in code and has none. The definition crosses as its own JSON rather than as contract-typed fields, because the runtime parses it with the agent schema's tolerant shape and re-typing it here would drop the keys that shape accepts and this one does not. The projected `__nativeCustomAgent` view the runtime derives is included, so a caller reading the declared model and a caller rendering the agent see the same definition. + /// 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. + /// The to monitor for cancellation requests. The default is . + /// One shipped agent's definition. + internal async Task GetBuiltinDefinitionAsync(string name, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(name); + + var request = new AgentsGetBuiltinDefinitionRequest { Name = name }; + return await CopilotClient.InvokeRpcAsync(_rpc, "agents.getBuiltinDefinition", [request], cancellationToken); + } + + /// Projects one shipped agent the way a picker lists it, reading only the metadata at the head of the definition file and stopping before the prompt body. `getBuiltinDefinition` answers the whole definition instead, so a client listing every shipped agent should prefer this one: the cost of a listing grows with the number of agents, and the prompt body is the part a listing never shows. The two also differ in shape. This returns the projected custom agent on its own, whereas `getBuiltinDefinition` returns the authored definition with that projection nested under `__nativeCustomAgent`. + /// 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. + /// The to monitor for cancellation requests. The default is . + /// One shipped agent, projected for a listing. + internal async Task GetBuiltinListingDefinitionAsync(string name, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(name); + + var request = new AgentsGetBuiltinListingDefinitionRequest { Name = name }; + return await CopilotClient.InvokeRpcAsync(_rpc, "agents.getBuiltinListingDefinition", [request], cancellationToken); + } + + /// Resolves the model a custom agent asks for against the models actually available, and answers both the model to switch to and the warning a user should see when the agent's preference cannot be met. A custom agent may name several acceptable models in preference order, so the decision is a match rather than a lookup, and an agent whose preference is unavailable is a normal outcome that produces a warning rather than an error. A host must call this rather than pick the first available name itself, because the preference order and the wording of the warning are what keep one installation's agent selection the same as another's. + /// The agent's declared `model:` entry, serialized. A single name or an ordered list of acceptable names. + /// The models available to this session, serialized in the shape the model list carries. + /// The to monitor for cancellation requests. The default is . + /// The model to switch to, and the warning to show when the agent's preference could not be met. + internal async Task CustomAgentInitialModelDecisionAsync(string agentModelsJson, string availableModelsJson, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(agentModelsJson); + ArgumentNullException.ThrowIfNull(availableModelsJson); + + var request = new AgentsCustomAgentInitialModelDecisionParams { AgentModelsJson = agentModelsJson, AvailableModelsJson = availableModelsJson }; + return await CopilotClient.InvokeRpcAsync(_rpc, "agents.customAgentInitialModelDecision", [request], cancellationToken); + } } /// Provides server-scoped Instructions APIs. @@ -42014,6 +45013,49 @@ public async Task GetDiscoveryPathsAsync(IListProvides server-scoped GlobalState APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerGlobalStateApi +{ + private readonly JsonRpc _rpc; + + internal ServerGlobalStateApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Reads the host's machine-wide state: which plugins are installed and the one-off flags and timestamps that record what the user has already been shown or migrated. This is the state that outlives a single session and a single workspace, so a host reads it to decide whether to run a first-launch step, offer an onboarding prompt, or skip one it has already completed. The stored credentials are deliberately not part of this result; a caller that needs an authenticated identity asks the account methods for it instead. Reading is non-destructive and every field is optional, because a fresh install has recorded nothing yet. + /// The to monitor for cancellation requests. The default is . + /// 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. + internal async Task LoadAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "globalState.load", [], cancellationToken); + } + + /// Reads the host's machine-wide state exactly as `globalState.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 state read matches the directory that session actually uses. An absent or empty `configDir` resolves the server's own home, making this identical to `globalState.load`. The stored credentials are omitted here for the same reason they are omitted from `globalState.load`: a caller that needs an authenticated identity asks the account methods instead, so pointing this at another directory cannot be used to read the credentials kept in it. + /// 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. + /// The to monitor for cancellation requests. The default is . + /// 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. + internal async Task LoadForConfigDirAsync(string? configDir = null, CancellationToken cancellationToken = default) + { + var request = new GlobalStateLoadForConfigDirRequest { ConfigDir = configDir }; + return await CopilotClient.InvokeRpcAsync(_rpc, "globalState.loadForConfigDir", [request], cancellationToken); + } + + /// Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appInstallNudgeResponded`, `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. + /// Top-level key to write, named as it appears in the result of `globalState.load`. It must be one of the writable keys that `globalState.writeKey` lists. + /// Copilot configuration directory to write the state document in, taking precedence over the server's own `COPILOT_HOME` and default home. Omit it, or pass an empty string, to write the directory the server resolved for itself. Mirrors `globalState.loadForConfigDir`, so a caller can read and write the same directory. + /// Value to store for the key. Omit it, or pass null, to remove the key instead. + /// The to monitor for cancellation requests. The default is . + internal async Task WriteKeyAsync(string key, string? configDir = null, object? value = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(key); + + var request = new GlobalStateWriteKeyRequest { Key = key, ConfigDir = configDir, Value = CopilotClient.ToJsonElementForWire(value) }; + await CopilotClient.InvokeRpcAsync(_rpc, "globalState.writeKey", [request], cancellationToken); + } +} + /// Provides server-scoped Commands APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class ServerCommandsApi @@ -42063,31 +45105,138 @@ internal ServerUserSettingsApi(JsonRpc rpc) _rpc = rpc; } - /// Drops this runtime process's in-memory user settings cache so the next settings read observes disk. - /// The to monitor for cancellation requests. The default is . - public async Task ReloadAsync(CancellationToken cancellationToken = default) - { - await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.reload", [], cancellationToken); - } - - /// Lists every known user setting (settings.json overlaid with the legacy config.json, config.json wins), 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. + /// 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 (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. + /// 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. Returns the keys whose new value is shadowed by a legacy config.json entry (config.json wins on read), which the runtime leaves in place — such writes do not take effect until the legacy value is removed. + /// 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 . - /// Outcome of writing user settings. - public async Task SetAsync(object settings, CancellationToken cancellationToken = default) + public async Task SetAsync(object settings, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(settings); var request = new UserSettingsSetRequest { Settings = CopilotClient.ToJsonElementForWire(settings)!.Value }; - return await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.set", [request], cancellationToken); + await CopilotClient.InvokeRpcAsync(_rpc, "user.settings.set", [request], cancellationToken); + } +} + +/// Provides server-scoped GitHubRepository APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerGitHubRepositoryApi +{ + private readonly JsonRpc _rpc; + + internal ServerGitHubRepositoryApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Resolves the GitHub repository that owns a working-tree path by reading the selected git remote configured for it, preferring `origin`. Returns a null `repository` when the path is inside a git working tree but that selected remote does not resolve to a GitHub host. Fails when the path is not inside a git working tree at all, so a caller can tell 'not a repository' apart from 'a repository with no GitHub remote'. + /// Absolute path to a directory inside the git working tree to resolve. + /// The to monitor for cancellation requests. The default is . + /// The GitHub repository that owns the requested path, when the selected remote (`origin`, else the first) is on a GitHub host. + internal async Task AtPathAsync(string path, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(path); + + var request = new GitHubRepositoryAtPathRequest { Path = path }; + return await CopilotClient.InvokeRpcAsync(_rpc, "gitHubRepository.atPath", [request], cancellationToken); + } +} + +/// Provides server-scoped GitHubOwners APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerGitHubOwnersApi +{ + private readonly JsonRpc _rpc; + + internal ServerGitHubOwnersApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// 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. + /// The to monitor for cancellation requests. The default is . + /// 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. + internal async Task NextRequestIdAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "gitHubOwners.nextRequestId", [], cancellationToken); + } + + /// 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`. + /// 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. + /// 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) + { + ArgumentNullException.ThrowIfNull(authInfo); + + var request = new GitHubOwnersListRequest { RequestId = requestId, AuthInfo = CopilotClient.ToJsonElementForWire(authInfo)!.Value }; + return await CopilotClient.InvokeRpcAsync(_rpc, "gitHubOwners.list", [request], cancellationToken); + } + + /// Abandons an owner listing started with the given request id. Answers `canceled: true` while a listing with that id is running. Answers `canceled: false` when the id was never registered, was registered but not used, was released after being abandoned, or its listing has ended. Canceling an unused id releases it, and a later `list` with that id is refused. The cancel acts only on owner listings and never reaches another request of the host. + /// Request id the listing was started with. + /// The to monitor for cancellation requests. The default is . + /// Whether the id named a running owner listing. + internal async Task CancelAsync(long requestId, CancellationToken cancellationToken = default) + { + var request = new GitHubOwnersCancelRequest { RequestId = requestId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "gitHubOwners.cancel", [request], cancellationToken); + } +} + +/// Provides server-scoped Git APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ServerGitApi +{ + private readonly JsonRpc _rpc; + + internal ServerGitApi(JsonRpc rpc) + { + _rpc = rpc; + } + + /// Reads the remote that the branch checked out in a working tree tracks, as `branch.<name>.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. + /// Absolute path to a directory inside the git working tree to query. + /// The to monitor for cancellation requests. The default is . + /// The remote the checked-out branch tracks. + internal async Task CurrentBranchRemoteAsync(string cwd, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(cwd); + + var request = new GitCwdRequest { Cwd = cwd }; + return await CopilotClient.InvokeRpcAsync(_rpc, "git.currentBranchRemote", [request], cancellationToken); + } + + /// 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. + /// Absolute path to a directory inside the git working tree to query. + /// The to monitor for cancellation requests. The default is . + /// Updated working directory and git context. Emitted as the new payload of `session.context_changed`. + internal async Task WorkingDirectoryContextAsync(string cwd, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(cwd); + + var request = new GitCwdRequest { Cwd = cwd }; + return await CopilotClient.InvokeRpcAsync(_rpc, "git.workingDirectoryContext", [request], cancellationToken); + } + + /// 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. + /// Absolute path to the root of the git working tree. + /// The to monitor for cancellation requests. The default is . + /// The GitHub repositories a working tree's remotes point at. + internal async Task ReposFromRemotesAsync(string gitRoot, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(gitRoot); + + var request = new GitReposFromRemotesRequest { GitRoot = gitRoot }; + return await CopilotClient.InvokeRpcAsync(_rpc, "git.reposFromRemotes", [request], cancellationToken); } } @@ -42121,14 +45270,19 @@ public async Task ClearCacheAsync(CancellationToken cancellationToken = default) /// 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, CancellationToken cancellationToken = default) + 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 }; + 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. @@ -42535,6 +45689,54 @@ public async Task EnrichMetadataAsync(IList(_rpc, "sessions.enrichMetadata", [request], cancellationToken); } + /// 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. + /// Session ID the workspace record belongs to. + /// 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. + /// `windows` (any letter case) selects Windows path rules. Any other value selects POSIX path rules. + /// 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`. + /// User-supplied display name for the workspace. + /// The to monitor for cancellation requests. The default is . + /// The workspace record that was written. + internal async Task CreateWorkspaceAsync(string sessionId, string sessionStatePath, string convention, SessionWorkingDirectoryContextWithClient? context = null, string? name = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(sessionId); + ArgumentNullException.ThrowIfNull(sessionStatePath); + ArgumentNullException.ThrowIfNull(convention); + + var request = new SessionsCreateWorkspaceRequest { SessionId = sessionId, SessionStatePath = sessionStatePath, Convention = convention, Context = context, Name = name }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sessions.createWorkspace", [request], cancellationToken); + } + + /// 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. + /// Root directory every session's state directory sits under. + /// 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. + /// The to monitor for cancellation requests. The default is . + /// The workspace record on disk, omitted when the session has none. + internal async Task LoadWorkspaceAsync(string sessionsHome, string sessionId, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(sessionsHome); + ArgumentNullException.ThrowIfNull(sessionId); + + var request = new SessionsLoadWorkspaceRequest { SessionsHome = sessionsHome, SessionId = sessionId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sessions.loadWorkspace", [request], cancellationToken); + } + + /// 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`. + /// Root directory every session's state directory sits under. + /// 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. + /// 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. + /// The to monitor for cancellation requests. The default is . + /// The merge completed. The record carries the supplied workspace-schema fields, but a stored `fork_count` stays. + internal async Task UpdateWorkspaceFieldsAsync(string sessionsHome, string sessionId, string fieldsJson, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(sessionsHome); + ArgumentNullException.ThrowIfNull(sessionId); + ArgumentNullException.ThrowIfNull(fieldsJson); + + var request = new SessionsUpdateWorkspaceFieldsRequest { SessionsHome = sessionsHome, SessionId = sessionId, FieldsJson = fieldsJson }; + return await CopilotClient.InvokeRpcAsync(_rpc, "sessions.updateWorkspaceFields", [request], cancellationToken); + } + /// Reloads user, plugin, and (optionally) repo hooks on the active session. /// Active session ID to reload hooks for. /// When true, skip repo-level hooks. Use before folder trust is confirmed; loadDeferredRepoHooks loads them post-trust. @@ -42681,35 +45883,55 @@ public async Task SpawnAsync(string cwd, string? agent } } -/// Provides server-scoped Accounts APIs. +/// Provides server-scoped Connectors APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] -public sealed class ServerAccountsApi +public sealed class ServerConnectorsApi { private readonly JsonRpc _rpc; - internal ServerAccountsApi(JsonRpc rpc) + internal ServerConnectorsApi(JsonRpc rpc) { _rpc = rpc; } - /// Acquire a Microsoft Entra access token through the runtime's OneAuth broker. Account-scoped because it uses the same native broker as the account stack: a trusted host application mints a scoped Entra token for its own use, most notably to authenticate to a remote MCP server whose authorization server is Entra ID (in place of the generic browser-OAuth flow). - /// Public client application id. - /// Tenant id or tenant selector, such as common or organizations. - /// Broker redirect URI registered for the client. Required: the OneAuth broker validates a non-empty, registered redirect URI for the public client (MSAL broker registration), so this is not a browser-flow vestige and cannot be omitted. - /// Exact delegated scopes to request. - /// Whether the broker may show interaction. - /// Previously rejected token that OneAuth must bypass during renewal. + /// Returns feature availability. /// The to monitor for cancellation requests. The default is . - /// Result of a OneAuth token acquisition. - internal async Task AcquireEntraTokenAsync(string clientId, string tenantId, string redirectUri, IList scopes, EntraTokenInteraction interaction, string? accessTokenToRenew = null, CancellationToken cancellationToken = default) + /// Feature availability. + public async Task GetCapabilitiesAsync(CancellationToken cancellationToken = default) { - ArgumentNullException.ThrowIfNull(clientId); - ArgumentNullException.ThrowIfNull(tenantId); - ArgumentNullException.ThrowIfNull(redirectUri); - ArgumentNullException.ThrowIfNull(scopes); + return await CopilotClient.InvokeRpcAsync(_rpc, "connectors.getCapabilities", [], cancellationToken); + } + + /// Returns eligible accounts. + /// The to monitor for cancellation requests. The default is . + /// Eligible accounts. + public async Task GetAccountsAsync(CancellationToken cancellationToken = default) + { + return await CopilotClient.InvokeRpcAsync(_rpc, "connectors.getAccounts", [], cancellationToken); + } + + /// Lists entries for the selected account. + /// Opaque account ID. + /// The to monitor for cancellation requests. The default is . + /// Entries for the selected account. + public async Task ListAsync(string accountId, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(accountId); + + var request = new ConnectorDiscoveryAccountRequest { AccountId = accountId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "connectors.list", [request], cancellationToken); + } + + /// Refreshes entries for the selected account. + /// Opaque account ID. + /// The to monitor for cancellation requests. The default is . + /// Entries for the selected account. + public async Task RefreshAsync(string accountId, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(accountId); - var request = new EntraTokenAcquireRequest { ClientId = clientId, TenantId = tenantId, RedirectUri = redirectUri, Scopes = scopes, Interaction = interaction, AccessTokenToRenew = accessTokenToRenew }; - return await CopilotClient.InvokeRpcAsync(_rpc, "accounts.acquireEntraToken", [request], cancellationToken); + var request = new ConnectorDiscoveryAccountRequest { AccountId = accountId }; + return await CopilotClient.InvokeRpcAsync(_rpc, "connectors.refresh", [request], cancellationToken); } } @@ -42725,6 +45947,12 @@ internal SessionRpc(CopilotSession session) internal CopilotSession Session => _session; + /// Providers APIs. + public ProvidersApi Providers => + field ?? + Interlocked.CompareExchange(ref field, new(_session), null) ?? + field; + /// Sandbox APIs. public SandboxApi Sandbox => field ?? @@ -42971,6 +46199,12 @@ internal SessionRpc(CopilotSession session) Interlocked.CompareExchange(ref field, new(_session), null) ?? field; + /// Quota APIs. + public QuotaApi Quota => + field ?? + Interlocked.CompareExchange(ref field, new(_session), null) ?? + field; + /// LimitPrediction APIs. public LimitPredictionApi LimitPrediction => field ?? @@ -43142,6 +46376,102 @@ public async Task LogAsync(string message, SessionLogLevel? level = n } } +/// Provides session-scoped Providers APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProvidersApi +{ + private readonly CopilotSession _session; + + internal ProvidersApi(CopilotSession session) + { + _session = session; + } + + /// Returns adapter definitions and supported operations in this session's effective provider catalog, without running discovery. Does not list provider instances or select inference models. + /// The to monitor for cancellation requests. The default is . + /// Normalized model-provider adapter definitions available to the session, not discovered instances. + public async Task GetCatalogAsync(CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionProvidersGetCatalogRequest { SessionId = _session.SessionId }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.providers.getCatalog", [request], cancellationToken); + } + + /// Discovers reachable instances using an adapter from this session's effective provider catalog and provider-specific discovery input. + /// Opaque adapter identity returned by `session.providers.getCatalog`. + /// 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. + /// The to monitor for cancellation requests. The default is . + /// Provider instances found by a discovery operation. + public async Task DiscoverAsync(string adapterId, object? input = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(adapterId); + _session.ThrowIfDisposed(); + + var request = new ModelProviderDiscoverRequest { SessionId = _session.SessionId, AdapterId = adapterId, Input = CopilotClient.ToJsonElementForWire(input) }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.providers.discover", [request], cancellationToken); + } + + /// Gets current health and version information for a discovered model-provider instance. + /// Provider instance reference returned by discovery. + /// The to monitor for cancellation requests. The default is . + /// Current health information for a provider instance. + public async Task GetStatusAsync(ModelProviderInstanceReference instance, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(instance); + _session.ThrowIfDisposed(); + + var request = new ModelProviderGetStatusRequest { SessionId = _session.SessionId, Instance = instance }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.providers.getStatus", [request], cancellationToken); + } + + /// Models APIs. + public ProvidersModelsApi Models => + field ?? + Interlocked.CompareExchange(ref field, new(_session), null) ?? + field; +} + +/// Provides session-scoped ProvidersModels APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class ProvidersModelsApi +{ + private readonly CopilotSession _session; + + internal ProvidersModelsApi(CopilotSession session) + { + _session = session; + } + + /// Lists models installed or otherwise available from a discovered model-provider instance. + /// Provider instance reference returned by discovery. + /// The to monitor for cancellation requests. The default is . + /// 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. + public async Task ListAsync(ModelProviderInstanceReference instance, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(instance); + _session.ThrowIfDisposed(); + + var request = new ModelProviderModelsListRequest { SessionId = _session.SessionId, Instance = instance }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.providers.models.list", [request], cancellationToken); + } + + /// Translates a discovered model into the provider and model configuration needed to use it, and reports whether each is already registered in this session. Prepares only: it registers nothing, writes nothing, and performs no provider requests. + /// The discovered instance that serves the model. + /// The discovered model to configure. + /// The to monitor for cancellation requests. The default is . + /// 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. + public async Task PrepareConfigurationAsync(ModelProviderInstance instance, DiscoveredModel model, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(instance); + ArgumentNullException.ThrowIfNull(model); + _session.ThrowIfDisposed(); + + var request = new ModelProviderPrepareConfigurationRequest { SessionId = _session.SessionId, Instance = instance, Model = model }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.providers.models.prepareConfiguration", [request], cancellationToken); + } +} + /// Provides session-scoped Sandbox APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class SandboxApi @@ -43892,7 +47222,7 @@ public async Task GetCurrentAsync(CancellationToken cancellationTo } /// Switches the session to a model and optional reasoning configuration. - /// Model selection id to switch to, as returned by `list`. A bare id (e.g. `claude-sonnet-4.6`) names a Copilot (CAPI) model; a provider-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a registry BYOK model. + /// 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. /// 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`. /// 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. /// Reasoning summary mode to request for supported model clients. @@ -43914,10 +47244,23 @@ public async Task SwitchToAsync(string modelId, AutoTier? a ArgumentNullException.ThrowIfNull(modelId); _session.ThrowIfDisposed(); - var request = new ModelSwitchToRequest { SessionId = _session.SessionId, ModelId = modelId, AutoTier = autoTier, ReasoningEffort = reasoningEffort, ReasoningSummary = reasoningSummary, Verbosity = verbosity, ModelCapabilities = modelCapabilities, ContextTier = contextTier, Source = source, DeferIfModelChangeQueued = deferIfModelChangeQueued, CompactionDecision = compactionDecision, RunCompactionPreflight = runCompactionPreflight, RepoScope = repoScope, ModelChangeScope = modelChangeScope, RequireAvailable = requireAvailable, PickerPersistence = pickerPersistence }; + var request = new ModelSwitchToRequestWithSession { SessionId = _session.SessionId, ModelId = modelId, AutoTier = autoTier, ReasoningEffort = reasoningEffort, ReasoningSummary = reasoningSummary, Verbosity = verbosity, ModelCapabilities = modelCapabilities, ContextTier = contextTier, Source = source, DeferIfModelChangeQueued = deferIfModelChangeQueued, CompactionDecision = compactionDecision, RunCompactionPreflight = runCompactionPreflight, RepoScope = repoScope, ModelChangeScope = modelChangeScope, RequireAvailable = requireAvailable, PickerPersistence = pickerPersistence }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.model.switchTo", [request], cancellationToken); } + /// Switches the session to a model and optional reasoning configuration. + /// Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. + /// The to monitor for cancellation requests. The default is . + /// The model identifier active on the session after the switch. + public async Task SwitchToAsync(ModelSwitchToRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(request.ModelId); + _session.ThrowIfDisposed(); + var wireRequest = new ModelSwitchToRequestWithSession { SessionId = _session.SessionId, ModelId = request.ModelId, ProviderId = request.ProviderId, AutoTier = request.AutoTier, ReasoningEffort = request.ReasoningEffort, ReasoningSummary = request.ReasoningSummary, Verbosity = request.Verbosity, ModelCapabilities = request.ModelCapabilities, ContextTier = request.ContextTier, Source = request.Source, DeferIfModelChangeQueued = request.DeferIfModelChangeQueued, CompactionDecision = request.CompactionDecision, RunCompactionPreflight = request.RunCompactionPreflight, RepoScope = request.RepoScope, ModelChangeScope = request.ModelChangeScope, RequireAvailable = request.RequireAvailable, PickerPersistence = request.PickerPersistence }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.model.switchTo", [wireRequest], cancellationToken); + } + /// Requests an Auto preference change without changing the session's selected model. The latest unclaimed request wins; the runtime commits it only after a later prompt using the `auto` model mints a usable model and token pair. A `pending` response confirms that the request was accepted, not that it committed. Observe eventual success through `session.model_change`, failure through the ephemeral `session.auto_tier_switch_failed` event, or current unclaimed state through `session.model.getCurrent`. /// 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. /// Origin to record on the effective `session.model_change` event. Defaults to `sdk` when omitted. @@ -43937,6 +47280,7 @@ public async Task SwitchAutoTierAsync(AutoTier? autoT /// 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. /// Effective default Auto routing preference from user and managed settings. Applies only to fresh sessions and never replaces a per-session selection. /// Model selected by repository settings, when configured. + /// Provider owning the repository-selected model. Omitted repository provider identity retains legacy bare-model behavior. /// Reasoning effort selected by repository settings, when configured. /// Context tier selected by repository settings, when configured. /// 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. @@ -43946,14 +47290,27 @@ public async Task SwitchAutoTierAsync(AutoTier? autoT /// 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. /// The to monitor for cancellation requests. The default is . /// The model identifier active on the session after the switch. - internal async Task ApplyStartupOverlayAsync(string? deviceManagedModel = null, string? serverManagedModel = null, string? policyHelperModel = null, AutoTier? autoTier = null, string? repoModel = null, string? repoReasoningEffort = null, string? repoContextTier = null, string? repoAutoTier = null, string? cliModel = null, bool? deferredResume = null, string? managedReasoningEffort = null, string? managedContextTier = null, CancellationToken cancellationToken = default) + internal async Task ApplyStartupOverlayAsync(string? deviceManagedModel = null, string? serverManagedModel = null, string? policyHelperModel = null, AutoTier? autoTier = null, string? repoModel = null, string? repoModelProviderId = null, string? repoReasoningEffort = null, string? repoContextTier = null, string? repoAutoTier = null, string? cliModel = null, bool? deferredResume = null, string? managedReasoningEffort = null, string? managedContextTier = null, CancellationToken cancellationToken = default) { _session.ThrowIfDisposed(); - var request = new ModelApplyStartupOverlayRequest { SessionId = _session.SessionId, DeviceManagedModel = deviceManagedModel, ServerManagedModel = serverManagedModel, PolicyHelperModel = policyHelperModel, AutoTier = autoTier, RepoModel = repoModel, RepoReasoningEffort = repoReasoningEffort, RepoContextTier = repoContextTier, RepoAutoTier = repoAutoTier, CliModel = cliModel, DeferredResume = deferredResume, ManagedReasoningEffort = managedReasoningEffort, ManagedContextTier = managedContextTier }; + var request = new ModelApplyStartupOverlayRequest { SessionId = _session.SessionId, DeviceManagedModel = deviceManagedModel, ServerManagedModel = serverManagedModel, PolicyHelperModel = policyHelperModel, AutoTier = autoTier, RepoModel = repoModel, RepoModelProviderId = repoModelProviderId, RepoReasoningEffort = repoReasoningEffort, RepoContextTier = repoContextTier, RepoAutoTier = repoAutoTier, CliModel = cliModel, DeferredResume = deferredResume, ManagedReasoningEffort = managedReasoningEffort, ManagedContextTier = managedContextTier }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.model.applyStartupOverlay", [request], cancellationToken); } + /// Clears an unchanged startup model/provider seed before default-model resolution. Never clears a resumed or user-selected model. + /// The expectedModel parameter. + /// Provider captured with the startup seed; omission matches only an unqualified seed. + /// The to monitor for cancellation requests. The default is . + internal async Task ClearStartupSeedAsync(string expectedModel, string? expectedProviderId = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(expectedModel); + _session.ThrowIfDisposed(); + + var request = new SessionModelClearStartupSeedRequest { SessionId = _session.SessionId, ExpectedModel = expectedModel, ExpectedProviderId = expectedProviderId }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.model.clearStartupSeed", [request], cancellationToken); + } + /// Replaces or clears the host-supplied model allowlist for a running session. /// Exact model IDs to permit, or null to clear the host restriction. /// The to monitor for cancellation requests. The default is . @@ -44020,6 +47377,7 @@ public async Task GetAsync(CancellationToken cancellationToken = de /// Session whose plan-mode base state should be inherited. /// Whether a dedicated plan model is configured. /// Dedicated model to use in plan mode, when configured. + /// Provider owning planModel. Omit for legacy bare-model selection. /// Reasoning effort to use with the dedicated plan model. /// Context tier to use with the dedicated plan model. /// Explicit response to a model-switch compaction preflight. @@ -44029,11 +47387,11 @@ public async Task GetAsync(CancellationToken cancellationToken = de /// Action to perform when leaving plan mode. /// The to monitor for cancellation requests. The default is . /// Outcome of a session mode change, including any model switch it triggered and follow-up the host must perform. - public async Task SetAsync(SessionMode mode, SessionMode? expectedMode = null, string? inheritPlanBaseFromSessionId = null, bool? planModelConfigured = null, string? planModel = null, string? planReasoningEffort = null, string? planContextTier = null, string? compactionDecision = null, bool? restorePlanModel = null, bool? persistPlanSelection = null, ModelPickerSettingsContext? pickerSettingsContext = null, string? planExitAction = null, CancellationToken cancellationToken = default) + public async Task SetAsync(SessionMode mode, SessionMode? expectedMode = null, string? inheritPlanBaseFromSessionId = null, bool? planModelConfigured = null, string? planModel = null, string? planModelProviderId = null, string? planReasoningEffort = null, string? planContextTier = null, string? compactionDecision = null, bool? restorePlanModel = null, bool? persistPlanSelection = null, ModelPickerSettingsContext? pickerSettingsContext = null, string? planExitAction = null, CancellationToken cancellationToken = default) { _session.ThrowIfDisposed(); - var request = new ModeSetRequest { SessionId = _session.SessionId, Mode = mode, ExpectedMode = expectedMode, InheritPlanBaseFromSessionId = inheritPlanBaseFromSessionId, PlanModelConfigured = planModelConfigured, PlanModel = planModel, PlanReasoningEffort = planReasoningEffort, PlanContextTier = planContextTier, CompactionDecision = compactionDecision, RestorePlanModel = restorePlanModel, PersistPlanSelection = persistPlanSelection, PickerSettingsContext = pickerSettingsContext, PlanExitAction = planExitAction }; + var request = new ModeSetRequest { SessionId = _session.SessionId, Mode = mode, ExpectedMode = expectedMode, InheritPlanBaseFromSessionId = inheritPlanBaseFromSessionId, PlanModelConfigured = planModelConfigured, PlanModel = planModel, PlanModelProviderId = planModelProviderId, PlanReasoningEffort = planReasoningEffort, PlanContextTier = planContextTier, CompactionDecision = compactionDecision, RestorePlanModel = restorePlanModel, PersistPlanSelection = persistPlanSelection, PickerSettingsContext = pickerSettingsContext, PlanExitAction = planExitAction }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mode.set", [request], cancellationToken); } } @@ -44410,9 +47768,21 @@ public async Task DiffAsync(WorkspaceDiffMode mode, bool? i { _session.ThrowIfDisposed(); - var request = new WorkspacesDiffRequest { SessionId = _session.SessionId, Mode = mode, IgnoreWhitespace = ignoreWhitespace }; + var request = new WorkspacesDiffRequestWithSession { SessionId = _session.SessionId, Mode = mode, IgnoreWhitespace = ignoreWhitespace }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.workspaces.diff", [request], cancellationToken); } + + /// Computes a diff for the session workspace. Never rejects for a busy session: a `session`-mode diff that cannot read the session's file-change captures falls back to an unstaged git diff with `isFallback: true` and reports why in `unavailableReason`. + /// Parameters for computing a workspace diff. + /// The to monitor for cancellation requests. The default is . + /// Workspace diff result for the requested mode. + public async Task DiffAsync(WorkspacesDiffRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + _session.ThrowIfDisposed(); + var wireRequest = new WorkspacesDiffRequestWithSession { SessionId = _session.SessionId, Mode = request.Mode, IgnoreWhitespace = request.IgnoreWhitespace, IncludeContents = request.IncludeContents }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.workspaces.diff", [wireRequest], cancellationToken); + } } /// Provides session-scoped AutopilotObjective APIs. @@ -44497,14 +47867,15 @@ public async Task GetSourcesAsync(CancellationToke return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.instructions.getSources", [request], cancellationToken); } - /// Invalidates cached custom-instruction discovery so subsequent turns and source reads observe instruction files currently on disk. + /// For local sessions, invalidates instruction discovery and the model-facing prompt, then returns freshly discovered sources. The updated prompt takes effect on the next turn. Remote sessions must reload on their agent host instead. /// The to monitor for cancellation requests. The default is . - public async Task ReloadAsync(CancellationToken cancellationToken = default) + /// Instruction sources loaded for the session, in merge order. + public async Task ReloadAsync(CancellationToken cancellationToken = default) { _session.ThrowIfDisposed(); var request = new SessionInstructionsReloadRequest { SessionId = _session.SessionId }; - await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.instructions.reload", [request], cancellationToken); + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.instructions.reload", [request], cancellationToken); } } @@ -44519,15 +47890,15 @@ internal CustomizationsApi(CopilotSession session) _session = session; } - /// Reloads all repository and user customizations for the active session: instructions, plugins and their MCP servers and hooks, custom agents, extensions, and skills. Returns diagnostics from the final skill reload. + /// For local sessions, reconciles repository context and discovered instructions, plugins, skills, agents, hooks, MCP servers, and extensions after files appear or change under the working directory. Independent component failures are returned in outcomes and errors; a rejected call can have partially applied earlier steps. Remote sessions must reload on their agent host instead. The model-facing context is rebuilt on the next turn. /// The to monitor for cancellation requests. The default is . - /// Diagnostics from reloading skill definitions, with warnings and errors as separate lists. - public async Task ReloadAsync(CancellationToken cancellationToken = default) + /// 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. + public async Task ReloadAsync(CancellationToken cancellationToken = default) { _session.ThrowIfDisposed(); var request = new SessionCustomizationsReloadRequest { SessionId = _session.SessionId }; - return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.customizations.reload", [request], cancellationToken); + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.customizations.reload", [request], cancellationToken); } } @@ -44920,7 +48291,18 @@ internal McpApi(CopilotSession session) _session = session; } - /// Lists MCP servers configured for the session, their connection status, and host-level state. The host-level state (disabled/filtered servers, failed/needs-auth/pending connections, mcp3p policy, full config) is empty/zero when no MCP host has been initialized for the session. + /// Records the IDE the host is connected to, so the agent's system prompt can name it and its workspace folder. Null or an omitted `ide` clears the recorded value, which is how a host reports that it is disconnected; there is no separate clear method. Both `ideName` and `workspaceFolder` are required together, because half a state cannot be attributed to a project. + /// The connected IDE. Null or omitted clears the recorded IDE, which is how a host reports that it is disconnected. + /// The to monitor for cancellation requests. The default is . + internal async Task SetConnectedIdeInfoAsync(SessionConnectedIdeInfo? ide = null, CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionMcpSetConnectedIdeInfoParams { SessionId = _session.SessionId, Ide = ide }; + await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.setConnectedIdeInfo", [request], cancellationToken); + } + + /// Lists materialized MCP servers and their connection status. Cache misses may start and wait for MCP servers. /// The to monitor for cancellation requests. The default is . /// MCP servers configured for the session, with their connection status and host-level state. public async Task ListAsync(CancellationToken cancellationToken = default) @@ -44931,6 +48313,17 @@ public async Task ListAsync(CancellationToken cancellationToken = return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.list", [request], cancellationToken); } + /// Lists effective MCP configuration without starting, restarting, authenticating, or waiting for servers. An optional live observation is from an already materialized matching server; this is not a readiness guarantee. + /// The to monitor for cancellation requests. The default is . + /// Effective MCP configuration with optional live observations from matching already materialized servers. + public async Task ListConfiguredAsync(CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionMcpListConfiguredRequest { SessionId = _session.SessionId }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.listConfigured", [request], cancellationToken); + } + /// Lists the tools exposed by a connected MCP server on this session's host. This performs a live `tools/list` request. Tool UI metadata is returned independently of whether MCP Apps rendering is enabled for the session. /// Name of the connected MCP server whose tools to list. /// The to monitor for cancellation requests. The default is . @@ -45224,6 +48617,12 @@ public async Task IsServerRunningAsync(string serverNa field ?? Interlocked.CompareExchange(ref field, new(_session), null) ?? field; + + /// Prompts APIs. + public McpPromptsApi Prompts => + field ?? + Interlocked.CompareExchange(ref field, new(_session), null) ?? + field; } /// Provides session-scoped McpOauth APIs. @@ -45303,7 +48702,7 @@ public async Task LoginAsync(string serverName, bool? force } /// Starts OAuth authentication for a remote MCP server. Owned servers require the original one-use prepareLogin handle and exact installation ID; manual servers retain the existing direct login behaviour. - /// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback success-page copy, and static OAuth client selection. + /// Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback handling, and static OAuth client selection. /// The to monitor for cancellation requests. The default is . /// OAuth authorization URL the caller should open, or empty when cached tokens already authenticated the server. public async Task LoginAsync(McpOauthLoginRequest request, CancellationToken cancellationToken = default) @@ -45311,10 +48710,24 @@ public async Task LoginAsync(McpOauthLoginRequest request, ArgumentNullException.ThrowIfNull(request); ArgumentNullException.ThrowIfNull(request.ServerName); _session.ThrowIfDisposed(); - var wireRequest = new McpOauthLoginRequestWithSession { SessionId = _session.SessionId, ServerName = request.ServerName, ForceReauth = request.ForceReauth, ClientName = request.ClientName, CallbackSuccessMessage = request.CallbackSuccessMessage, ClientId = request.ClientId, ClientSecret = request.ClientSecret, PublicClient = request.PublicClient, GrantType = request.GrantType, LoginId = request.LoginId, ExpectedInstallationId = request.ExpectedInstallationId }; + var wireRequest = new McpOauthLoginRequestWithSession { SessionId = _session.SessionId, ServerName = request.ServerName, ForceReauth = request.ForceReauth, ClientName = request.ClientName, CallbackSuccessMessage = request.CallbackSuccessMessage, ClientId = request.ClientId, ClientSecret = request.ClientSecret, PublicClient = request.PublicClient, GrantType = request.GrantType, RedirectUri = request.RedirectUri, LoginId = request.LoginId, ExpectedInstallationId = request.ExpectedInstallationId }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.oauth.login", [wireRequest], cancellationToken); } + /// Completes a runtime-managed MCP OAuth login after the authorization server redirects to a host-managed callback URL. + /// Opaque identifier returned by session.mcp.oauth.login for the pending external callback. + /// 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. + /// The to monitor for cancellation requests. The default is . + public async Task CompleteAsync(string authorizationId, string callbackUrl, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(authorizationId); + ArgumentNullException.ThrowIfNull(callbackUrl); + _session.ThrowIfDisposed(); + + var request = new McpOauthCompleteRequest { SessionId = _session.SessionId, AuthorizationId = authorizationId, CallbackUrl = callbackUrl }; + await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.oauth.complete", [request], cancellationToken); + } + /// Passively probes a configured remote MCP server to classify whether OAuth is required or a cached/override token is accepted. Does not start OAuth, emit pending OAuth requests, or mutate MCP connection state. /// Name of the configured remote MCP server to probe. /// The to monitor for cancellation requests. The default is . @@ -45548,6 +48961,48 @@ public async Task ListTemplatesAsync(string ser } } +/// Provides session-scoped McpPrompts APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class McpPromptsApi +{ + private readonly CopilotSession _session; + + internal McpPromptsApi(CopilotSession session) + { + _session = session; + } + + /// Enumerate one page of prompts a connected MCP server exposes (proxies MCP `prompts/list`). Pass `cursor` to continue from a prior result's `nextCursor`. + /// Name of the MCP server whose prompts to enumerate. + /// Opaque MCP pagination cursor from a prior `nextCursor` value. + /// The to monitor for cancellation requests. The default is . + /// One page of prompts advertised by the named MCP server. + public async Task ListAsync(string serverName, string? cursor = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(serverName); + _session.ThrowIfDisposed(); + + var request = new McpPromptsListRequest { SessionId = _session.SessionId, ServerName = serverName, Cursor = cursor }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.prompts.list", [request], cancellationToken); + } + + /// Get a prompt's messages from a connected MCP server (proxies MCP `prompts/get`). Content is preserved as opaque JSON. Does not send messages to the model, execute tools, or fetch referenced resources. + /// Name of the MCP server hosting the prompt. + /// The programmatic name of the prompt. + /// String-valued arguments to pass to the prompt. + /// The to monitor for cancellation requests. The default is . + /// Prompt messages returned by the MCP server without sending them to the model. + public async Task GetAsync(string serverName, string promptName, IDictionary? arguments = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(serverName); + ArgumentNullException.ThrowIfNull(promptName); + _session.ThrowIfDisposed(); + + var request = new McpPromptsGetRequest { SessionId = _session.SessionId, ServerName = serverName, PromptName = promptName, Arguments = arguments }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.mcp.prompts.get", [request], cancellationToken); + } +} + /// Provides session-scoped Diagnostics APIs. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed class DiagnosticsApi @@ -45891,6 +49346,18 @@ public async Task ReloadAsync(SessionPluginsReloadRequest? request = null, Cance await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.plugins.reload", [rpcRequest], cancellationToken); } + /// Installs or updates the plugins the session's managed settings require now, without waiting for the next automatic attempt after a failure, then reloads plugins. Reports progress and failures with the same `session.info` and `session.warning` events as automatic preparation. Fails when the runtime does not prepare managed plugins for the session. + /// Plugins required by the session's managed settings to install or update again. + /// The to monitor for cancellation requests. The default is . + /// Per-plugin outcomes of retrying the plugins required by the session's managed settings. + public async Task RetryManagedAsync(SessionPluginsRetryManagedRequest? request = null, CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var rpcRequest = new SessionPluginsRetryManagedRequestWithSession { SessionId = _session.SessionId, Plugins = request?.Plugins }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.plugins.retryManaged", [rpcRequest], cancellationToken); + } + /// Marketplaces APIs. public PluginsMarketplacesApi Marketplaces => field ?? @@ -46167,7 +49634,7 @@ public async Task ListAsync(CancellationToken cancellationToken = return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.extensions.list", [request], cancellationToken); } - /// Enables an extension for the session. + /// Enables an extension for the session and persists the preference when the session has a settings store. Hosts synchronizing effective membership should use extensions.reconcile instead. /// Source-qualified extension ID to enable. /// The to monitor for cancellation requests. The default is . public async Task EnableAsync(string id, CancellationToken cancellationToken = default) @@ -46179,7 +49646,7 @@ public async Task EnableAsync(string id, CancellationToken cancellationToken = d await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.extensions.enable", [request], cancellationToken); } - /// Disables an extension for the session. + /// Disables an extension for the session and persists the preference when the session has a settings store. Hosts synchronizing effective membership should use extensions.reconcile instead. /// Source-qualified extension ID to disable. /// The to monitor for cancellation requests. The default is . public async Task DisableAsync(string id, CancellationToken cancellationToken = default) @@ -46201,6 +49668,17 @@ public async Task ReloadAsync(CancellationToken cancellationToken = default) await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.extensions.reload", [request], cancellationToken); } + /// Host-only reconciliation of authoritative session-effective extension membership and enablement. Refreshes runtime-owned discovery and preferences without persisting settings, installing plugins, or restarting unchanged activations. Returns ExtensionList only after required starts and process/contribution cleanup settle. Takes no caller inventory or overrides. Missing controllers, unready/incomplete discovery, unavailable workspaces, superseded inputs, and lifecycle failures are errors, not empty membership. Independently proven revocations may be applied before an error; retry converges without restarting healthy activations. Error data contains lifecycleChangesApplied and code: extension_reconciliation_host_required, extension_reconciliation_unavailable, extension_reconciliation_not_ready, extension_reconciliation_discovery_failed, extension_reconciliation_workspace_unavailable, extension_reconciliation_superseded, or extension_reconciliation_lifecycle_failed. Mark host reconciliation state applied only on success. On older runtimes, method-not-found must not fall back to global discovery and persistent extension disables. + /// The to monitor for cancellation requests. The default is . + /// Extensions discovered for the session, with their current status. + public async Task ReconcileAsync(CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionExtensionsReconcileRequest { SessionId = _session.SessionId }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.extensions.reconcile", [request], cancellationToken); + } + /// Push attachments into the next user-message turn from an extension. The host should surface them as composer pills and forward them via the next session.send call. Callable only by extension-owned connections. /// Attachments to push into the next user-message turn. extension_context entries take the slim shape; standard variants take their full AttachmentSchema shape. /// 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. @@ -46290,6 +49768,17 @@ public async Task HandlePendingToolCallAsync(string return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.tools.handlePendingToolCall", [request], cancellationToken); } + /// Lists the external tool calls of the session and its sub-agents that are still waiting for session.tools.handlePendingToolCall. + /// The to monitor for cancellation requests. The default is . + /// External tool calls still waiting for a result. + public async Task ListPendingRequestsAsync(CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new ToolsListPendingRequestsRequest { SessionId = _session.SessionId }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.tools.listPendingRequests", [request], cancellationToken); + } + /// Resolves, builds, and validates the runtime tool list for the session. /// The to monitor for cancellation requests. The default is . /// 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. @@ -46541,6 +50030,21 @@ public async Task HandlePendingElicitationAsync(string requ return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.ui.handlePendingElicitation", [request], cancellationToken); } + /// Resolves a pending elicitation request after direct interaction in the trusted in-process client. Only an accepted response to the built-in ask_user tool can become trusted human evidence. + /// The unique request ID from the elicitation.requested event. + /// The elicitation response (accept with form values, decline, or cancel). + /// The to monitor for cancellation requests. The default is . + /// Indicates whether the elicitation response was accepted; false if it was already resolved by another client. + internal async Task HandleHumanAskUserAsync(string requestId, UIElicitationResponse result, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(requestId); + ArgumentNullException.ThrowIfNull(result); + _session.ThrowIfDisposed(); + + var request = new UIHandlePendingElicitationRequest { SessionId = _session.SessionId, RequestId = requestId, Result = result }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.ui.handleHumanAskUser", [request], cancellationToken); + } + /// Resolves a pending `user_input.requested` event with the user's response. /// The unique request ID from the user_input.requested event. /// User response for a pending user-input request, with answer text and whether it was typed freeform. @@ -46556,6 +50060,21 @@ public async Task HandlePendingUserInputAsync(string requ return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.ui.handlePendingUserInput", [request], cancellationToken); } + /// Resolves a pending `user_input.requested` event after direct interaction in the trusted in-process client. + /// The unique request ID from the user_input.requested event. + /// User response for a pending user-input request, with answer text and whether it was typed freeform. + /// The to monitor for cancellation requests. The default is . + /// Indicates whether the pending UI request was resolved by this call. + internal async Task HandleHumanUserInputAsync(string requestId, UIUserInputResponse response, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(requestId); + ArgumentNullException.ThrowIfNull(response); + _session.ThrowIfDisposed(); + + var request = new UIHandlePendingUserInputRequest { SessionId = _session.SessionId, RequestId = requestId, Response = response }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.ui.handleHumanUserInput", [request], cancellationToken); + } + /// Resolves a pending `sampling.requested` event with a sampling result, or rejects it. /// The unique request ID from the sampling.requested event. /// Optional sampling result payload. Omit to reject/cancel the sampling request without providing a result. @@ -46614,6 +50133,21 @@ public async Task HandlePendingExitPlanModeAsync(string r return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.ui.handlePendingExitPlanMode", [request], cancellationToken); } + /// Resolves a pending `exit_plan_mode.requested` event after direct interaction in the trusted in-process client. + /// The unique request ID from the exit_plan_mode.requested event. + /// User response for a pending exit-plan-mode request, with approval state, selected action, auto-approve flag, and feedback. + /// The to monitor for cancellation requests. The default is . + /// Indicates whether the pending UI request was resolved by this call. + internal async Task HandleHumanExitPlanModeAsync(string requestId, UIExitPlanModeResponse response, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(requestId); + ArgumentNullException.ThrowIfNull(response); + _session.ThrowIfDisposed(); + + var request = new UIHandlePendingExitPlanModeRequest { SessionId = _session.SessionId, RequestId = requestId, Response = response }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.ui.handleHumanExitPlanMode", [request], cancellationToken); + } + /// Registers an in-process handler for auto-mode-switch requests so the server bridge skips dispatch. /// The to monitor for cancellation requests. The default is . /// 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). @@ -47069,16 +50603,17 @@ public async Task ActivityAsync(CancellationToken cancellationT } /// Returns the token breakdown for the session's current context window for a given model. - /// Maximum prompt tokens allowed by the target model. Pass 0 to use the runtime default. - /// Maximum output tokens allowed by the target model. Pass 0 if unknown. + /// Advertised prompt allowance. Pass 0 to resolve the selected model and context tier from the session. + /// 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. /// Model identifier used for tokenization. Omit to use the session default. Used both for token counting and to compute display values. + /// Provider owning selectedModel. Omit to use the current provider for the current model, or deterministic legacy lookup for a different model. /// The to monitor for cancellation requests. The default is . /// Token breakdown for the session's current context window, or null if uninitialized. - public async Task ContextInfoAsync(long promptTokenLimit, long outputTokenLimit, string? selectedModel = null, CancellationToken cancellationToken = default) + public async Task ContextInfoAsync(long promptTokenLimit, long outputTokenLimit, string? selectedModel = null, string? providerId = null, CancellationToken cancellationToken = default) { _session.ThrowIfDisposed(); - var request = new MetadataContextInfoRequest { SessionId = _session.SessionId, PromptTokenLimit = promptTokenLimit, OutputTokenLimit = outputTokenLimit, SelectedModel = selectedModel }; + var request = new MetadataContextInfoRequest { SessionId = _session.SessionId, PromptTokenLimit = promptTokenLimit, OutputTokenLimit = outputTokenLimit, SelectedModel = selectedModel, ProviderId = providerId }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.metadata.contextInfo", [request], cancellationToken); } @@ -47217,12 +50752,12 @@ internal ShellApi(CopilotSession session) _session = session; } - /// Starts a shell command and streams output through session notifications. The command runs as the leader of its own process group (POSIX) or in a dedicated job object (Windows), so a forced termination — via "shell.kill", the request timeout, or session disposal — signals that whole group/job rather than only the direct child. Two gaps are worth planning for: a command that exits on its own does not trigger that teardown, and on POSIX a descendant that moves itself into a new session or process group (for example via "setsid") leaves the signalled group, so either can leave a background process running. + /// Starts a shell command, returning an RPC error if it cannot be spawned. The command runs as the leader of its own process group (POSIX) or in a dedicated job object (Windows), so a forced termination — via "shell.kill", the request timeout, or session disposal — signals that whole group/job rather than only the direct child. Two gaps are worth planning for: a command that exits on its own does not trigger that teardown, and on POSIX a descendant that moves itself into a new session or process group (for example via "setsid") leaves the signalled group, so either can leave a background process running. /// Shell command to execute. /// Working directory (defaults to session working directory). /// Timeout in milliseconds (default: 30000). /// The to monitor for cancellation requests. The default is . - /// Identifier of the spawned process, used to correlate streamed output and exit notifications. + /// Identifier of the spawned shell process, usable with shell.kill while the process is running. public async Task ExecAsync(string command, string? cwd = null, TimeSpan? timeout = null, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(command); @@ -47752,6 +51287,64 @@ public async Task GetMetricsAsync(CancellationToken cance var request = new SessionUsageGetMetricsRequest { SessionId = _session.SessionId }; return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.usage.getMetrics", [request], cancellationToken); } + + /// Internal host-only replacement of absolute code-change totals reported by a relay host. + /// Absolute added-line total, replacing the previous reading. + /// Absolute removed-line total, replacing the previous reading. + /// Absolute changed-file count; omission preserves the previous count. + /// The to monitor for cancellation requests. The default is . + internal async Task SetCodeChangesAsync(double linesAdded, double linesRemoved, double? filesCount = null, CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionUsageSetCodeChangesRequest { SessionId = _session.SessionId, LinesAdded = linesAdded, LinesRemoved = linesRemoved, FilesCount = filesCount }; + await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.usage.setCodeChanges", [request], cancellationToken); + } +} + +/// Provides session-scoped Quota APIs. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed class QuotaApi +{ + private readonly CopilotSession _session; + + internal QuotaApi(CopilotSession session) + { + _session = session; + } + + /// Gets the session's current quota and account projection without making a network request. + /// The to monitor for cancellation requests. The default is . + /// The incumbent session-owned quota and account projection. + public async Task GetAsync(CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionQuotaGetRequest { SessionId = _session.SessionId }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.quota.get", [request], cancellationToken); + } + + /// Refreshes the session's provider model catalog bypassing its cache, folds quota snapshots into session state, and returns the updated projection. Failures leave the last known quota intact. + /// The to monitor for cancellation requests. The default is . + /// The incumbent session-owned quota and account projection. + public async Task RefreshAsync(CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionQuotaRefreshRequest { SessionId = _session.SessionId }; + return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.quota.refresh", [request], cancellationToken); + } + + /// Returns and clears the session's pending quota warnings. Reading or refreshing quota does not drain warnings. + /// The to monitor for cancellation requests. The default is . + /// Pending warnings returned and cleared by session.quota.takeWarnings. + public async Task> TakeWarningsAsync(CancellationToken cancellationToken = default) + { + _session.ThrowIfDisposed(); + + var request = new SessionQuotaTakeWarningsRequest { SessionId = _session.SessionId }; + return await CopilotClient.InvokeRpcAsync>(_session.Rpc, "session.quota.takeWarnings", [request], cancellationToken); + } } /// Provides session-scoped LimitPrediction APIs. @@ -48040,11 +51633,21 @@ public interface ISessionFsHandler /// The to monitor for cancellation requests. The default is . /// File content as a UTF-8 string, or a filesystem error if the read failed. Task ReadFileAsync(SessionFsReadFileRequest request, CancellationToken cancellationToken = default); + /// Reads binary file content from the client-provided session filesystem. + /// Path of the binary file to read from the client-provided session filesystem. + /// The to monitor for cancellation requests. The default is . + /// File bytes as standard base64, or a filesystem error if the read failed. + Task ReadFileBytesAsync(SessionFsReadFileBytesRequest request, CancellationToken cancellationToken = default); /// Writes a file in the client-provided session filesystem. /// File path, content to write, and optional mode for the client-provided session filesystem. /// The to monitor for cancellation requests. The default is . /// Describes a filesystem error. Task WriteFileAsync(SessionFsWriteFileRequest request, CancellationToken cancellationToken = default); + /// Writes binary file content to the client-provided session filesystem. + /// File path, standard-base64-encoded bytes to write, and optional mode for the client-provided session filesystem. + /// The to monitor for cancellation requests. The default is . + /// Describes a filesystem error. + Task WriteFileBytesAsync(SessionFsWriteFileBytesRequest request, CancellationToken cancellationToken = default); /// Appends content to a file in the client-provided session filesystem, creating parent directories as needed. /// File path, content to append, and optional mode for the client-provided session filesystem. Implementations create parent directories as needed. /// The to monitor for cancellation requests. The default is . @@ -48181,12 +51784,24 @@ public static void RegisterClientSessionApiHandlers(JsonRpc rpc, Func>)(async (request, cancellationToken) => + { + var handler = getHandlers(request.SessionId).SessionFs; + if (handler is null) throw new InvalidOperationException($"No sessionFs handler registered for session: {request.SessionId}"); + return await handler.ReadFileBytesAsync(request, cancellationToken); + }), singleObjectParam: true); rpc.SetLocalRpcMethod("sessionFs.writeFile", (Func>)(async (request, cancellationToken) => { var handler = getHandlers(request.SessionId).SessionFs; if (handler is null) throw new InvalidOperationException($"No sessionFs handler registered for session: {request.SessionId}"); return await handler.WriteFileAsync(request, cancellationToken); }), singleObjectParam: true); + rpc.SetLocalRpcMethod("sessionFs.writeFileBytes", (Func>)(async (request, cancellationToken) => + { + var handler = getHandlers(request.SessionId).SessionFs; + if (handler is null) throw new InvalidOperationException($"No sessionFs handler registered for session: {request.SessionId}"); + return await handler.WriteFileBytesAsync(request, cancellationToken); + }), singleObjectParam: true); rpc.SetLocalRpcMethod("sessionFs.appendFile", (Func>)(async (request, cancellationToken) => { var handler = getHandlers(request.SessionId).SessionFs; @@ -48431,6 +52046,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.AgentInterruptedData), TypeInfoPropertyName = "SessionEventsAgentInterruptedData")] [JsonSerializable(typeof(GitHub.Copilot.AgentInterruptedEvent), TypeInfoPropertyName = "SessionEventsAgentInterruptedEvent")] [JsonSerializable(typeof(GitHub.Copilot.AgentModelPolicy), TypeInfoPropertyName = "SessionEventsAgentModelPolicy")] +[JsonSerializable(typeof(GitHub.Copilot.AiCreditsStatus), TypeInfoPropertyName = "SessionEventsAiCreditsStatus")] [JsonSerializable(typeof(GitHub.Copilot.AssistantFusionPhaseActivityEvent), TypeInfoPropertyName = "SessionEventsAssistantFusionPhaseActivityEvent")] [JsonSerializable(typeof(GitHub.Copilot.AssistantFusionPhaseCompletedEvent), TypeInfoPropertyName = "SessionEventsAssistantFusionPhaseCompletedEvent")] [JsonSerializable(typeof(GitHub.Copilot.AssistantFusionPhaseFailedEvent), TypeInfoPropertyName = "SessionEventsAssistantFusionPhaseFailedEvent")] @@ -48591,11 +52207,16 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.HookProgressEvent), TypeInfoPropertyName = "SessionEventsHookProgressEvent")] [JsonSerializable(typeof(GitHub.Copilot.HookStartData), TypeInfoPropertyName = "SessionEventsHookStartData")] [JsonSerializable(typeof(GitHub.Copilot.HookStartEvent), TypeInfoPropertyName = "SessionEventsHookStartEvent")] +[JsonSerializable(typeof(GitHub.Copilot.HumanResponseActor), TypeInfoPropertyName = "SessionEventsHumanResponseActor")] +[JsonSerializable(typeof(GitHub.Copilot.HumanResponseRecordedData), TypeInfoPropertyName = "SessionEventsHumanResponseRecordedData")] +[JsonSerializable(typeof(GitHub.Copilot.HumanResponseRecordedEvent), TypeInfoPropertyName = "SessionEventsHumanResponseRecordedEvent")] +[JsonSerializable(typeof(GitHub.Copilot.HumanResponseRecordedResponse), TypeInfoPropertyName = "SessionEventsHumanResponseRecordedResponse")] [JsonSerializable(typeof(GitHub.Copilot.IndexedSearchDisabledReason), TypeInfoPropertyName = "SessionEventsIndexedSearchDisabledReason")] [JsonSerializable(typeof(GitHub.Copilot.IndexedSearchErrorType), TypeInfoPropertyName = "SessionEventsIndexedSearchErrorType")] [JsonSerializable(typeof(GitHub.Copilot.IndexedSearchIncrementalPhase), TypeInfoPropertyName = "SessionEventsIndexedSearchIncrementalPhase")] [JsonSerializable(typeof(GitHub.Copilot.IndexedSearchOutcome), TypeInfoPropertyName = "SessionEventsIndexedSearchOutcome")] [JsonSerializable(typeof(GitHub.Copilot.IndexedSearchState), TypeInfoPropertyName = "SessionEventsIndexedSearchState")] +[JsonSerializable(typeof(GitHub.Copilot.ManagedPluginProgressPhase), TypeInfoPropertyName = "SessionEventsManagedPluginProgressPhase")] [JsonSerializable(typeof(GitHub.Copilot.ManagedSettingsEnforcedAction), TypeInfoPropertyName = "SessionEventsManagedSettingsEnforcedAction")] [JsonSerializable(typeof(GitHub.Copilot.ManagedSettingsEnforcedEscalation), TypeInfoPropertyName = "SessionEventsManagedSettingsEnforcedEscalation")] [JsonSerializable(typeof(GitHub.Copilot.ManagedSettingsResolvedSource), TypeInfoPropertyName = "SessionEventsManagedSettingsResolvedSource")] @@ -48642,8 +52263,11 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.ModelCallFinishedOutcome), TypeInfoPropertyName = "SessionEventsModelCallFinishedOutcome")] [JsonSerializable(typeof(GitHub.Copilot.ModelCallStartData), TypeInfoPropertyName = "SessionEventsModelCallStartData")] [JsonSerializable(typeof(GitHub.Copilot.ModelCallStartEvent), TypeInfoPropertyName = "SessionEventsModelCallStartEvent")] +[JsonSerializable(typeof(GitHub.Copilot.ModelCallWebSocketFallbackReason), TypeInfoPropertyName = "SessionEventsModelCallWebSocketFallbackReason")] [JsonSerializable(typeof(GitHub.Copilot.ModelChangeSource), TypeInfoPropertyName = "SessionEventsModelChangeSource")] [JsonSerializable(typeof(GitHub.Copilot.ModelDeselectedReason), TypeInfoPropertyName = "SessionEventsModelDeselectedReason")] +[JsonSerializable(typeof(GitHub.Copilot.ModelProviderKind), TypeInfoPropertyName = "SessionEventsModelProviderKind")] +[JsonSerializable(typeof(GitHub.Copilot.ModelProviderRef), TypeInfoPropertyName = "SessionEventsModelProviderRef")] [JsonSerializable(typeof(GitHub.Copilot.OmittedBinaryOmittedReason), TypeInfoPropertyName = "SessionEventsOmittedBinaryOmittedReason")] [JsonSerializable(typeof(GitHub.Copilot.OmittedBinaryResult), TypeInfoPropertyName = "SessionEventsOmittedBinaryResult")] [JsonSerializable(typeof(GitHub.Copilot.OmittedBinaryType), TypeInfoPropertyName = "SessionEventsOmittedBinaryType")] @@ -48717,12 +52341,22 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.PermissionRule), TypeInfoPropertyName = "SessionEventsPermissionRule")] [JsonSerializable(typeof(GitHub.Copilot.PermissionSandboxPathGrant), TypeInfoPropertyName = "SessionEventsPermissionSandboxPathGrant")] [JsonSerializable(typeof(GitHub.Copilot.PermissionSandboxPathGrantAccess), TypeInfoPropertyName = "SessionEventsPermissionSandboxPathGrantAccess")] +[JsonSerializable(typeof(GitHub.Copilot.PermissionWriteFileContent), TypeInfoPropertyName = "SessionEventsPermissionWriteFileContent")] +[JsonSerializable(typeof(GitHub.Copilot.PermissionWriteFileEdit), TypeInfoPropertyName = "SessionEventsPermissionWriteFileEdit")] [JsonSerializable(typeof(GitHub.Copilot.PersistedBinaryImage), TypeInfoPropertyName = "SessionEventsPersistedBinaryImage")] [JsonSerializable(typeof(GitHub.Copilot.PersistedBinaryImageType), TypeInfoPropertyName = "SessionEventsPersistedBinaryImageType")] [JsonSerializable(typeof(GitHub.Copilot.PersistedBinaryResult), TypeInfoPropertyName = "SessionEventsPersistedBinaryResult")] [JsonSerializable(typeof(GitHub.Copilot.PlanChangedOperation), TypeInfoPropertyName = "SessionEventsPlanChangedOperation")] [JsonSerializable(typeof(GitHub.Copilot.PromptCacheBreakData), TypeInfoPropertyName = "SessionEventsPromptCacheBreakData")] [JsonSerializable(typeof(GitHub.Copilot.PromptCacheBreakEvent), TypeInfoPropertyName = "SessionEventsPromptCacheBreakEvent")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaAccessState), TypeInfoPropertyName = "SessionEventsProviderQuotaAccessState")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaAcquisitionStatus), TypeInfoPropertyName = "SessionEventsProviderQuotaAcquisitionStatus")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaBudgetMetadata), TypeInfoPropertyName = "SessionEventsProviderQuotaBudgetMetadata")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaCapacityState), TypeInfoPropertyName = "SessionEventsProviderQuotaCapacityState")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaObservationKind), TypeInfoPropertyName = "SessionEventsProviderQuotaObservationKind")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaQuantityKind), TypeInfoPropertyName = "SessionEventsProviderQuotaQuantityKind")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaState), TypeInfoPropertyName = "SessionEventsProviderQuotaState")] +[JsonSerializable(typeof(GitHub.Copilot.ProviderQuotaUnit), TypeInfoPropertyName = "SessionEventsProviderQuotaUnit")] [JsonSerializable(typeof(GitHub.Copilot.ReasoningSummary), TypeInfoPropertyName = "SessionEventsReasoningSummary")] [JsonSerializable(typeof(GitHub.Copilot.RecommendedAutoTier), TypeInfoPropertyName = "SessionEventsRecommendedAutoTier")] [JsonSerializable(typeof(GitHub.Copilot.RemediationAction), TypeInfoPropertyName = "SessionEventsRemediationAction")] @@ -48826,6 +52460,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteData), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteData")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteError), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteError")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteEvent), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteEvent")] +[JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteFileEdit), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteFileEdit")] +[JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteFileEditKind), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteFileEditKind")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteResult), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteResult")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteShellExecution), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteShellExecution")] [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionCompleteToolDescription), TypeInfoPropertyName = "SessionEventsToolExecutionCompleteToolDescription")] @@ -48853,9 +52489,22 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(GitHub.Copilot.ToolExecutionStartToolDescriptionMetaUIVisibility), TypeInfoPropertyName = "SessionEventsToolExecutionStartToolDescriptionMetaUIVisibility")] [JsonSerializable(typeof(GitHub.Copilot.ToolSearchActivatedData), TypeInfoPropertyName = "SessionEventsToolSearchActivatedData")] [JsonSerializable(typeof(GitHub.Copilot.ToolSearchActivatedEvent), TypeInfoPropertyName = "SessionEventsToolSearchActivatedEvent")] +[JsonSerializable(typeof(GitHub.Copilot.ToolShellOutputData), TypeInfoPropertyName = "SessionEventsToolShellOutputData")] +[JsonSerializable(typeof(GitHub.Copilot.ToolShellOutputEvent), TypeInfoPropertyName = "SessionEventsToolShellOutputEvent")] +[JsonSerializable(typeof(GitHub.Copilot.ToolShellOutputStream), TypeInfoPropertyName = "SessionEventsToolShellOutputStream")] [JsonSerializable(typeof(GitHub.Copilot.ToolUserRequestedData), TypeInfoPropertyName = "SessionEventsToolUserRequestedData")] [JsonSerializable(typeof(GitHub.Copilot.ToolUserRequestedEvent), TypeInfoPropertyName = "SessionEventsToolUserRequestedEvent")] [JsonSerializable(typeof(GitHub.Copilot.UIEphemeralQueryPhase), TypeInfoPropertyName = "SessionEventsUIEphemeralQueryPhase")] +[JsonSerializable(typeof(GitHub.Copilot.UsageAccountingIdentity), TypeInfoPropertyName = "SessionEventsUsageAccountingIdentity")] +[JsonSerializable(typeof(GitHub.Copilot.UsageGetMetricsResult), TypeInfoPropertyName = "SessionEventsUsageGetMetricsResult")] +[JsonSerializable(typeof(GitHub.Copilot.UsageMetricsAgentMetric), TypeInfoPropertyName = "SessionEventsUsageMetricsAgentMetric")] +[JsonSerializable(typeof(GitHub.Copilot.UsageMetricsCodeChanges), TypeInfoPropertyName = "SessionEventsUsageMetricsCodeChanges")] +[JsonSerializable(typeof(GitHub.Copilot.UsageMetricsModelMetric), TypeInfoPropertyName = "SessionEventsUsageMetricsModelMetric")] +[JsonSerializable(typeof(GitHub.Copilot.UsageMetricsModelMetricRequests), TypeInfoPropertyName = "SessionEventsUsageMetricsModelMetricRequests")] +[JsonSerializable(typeof(GitHub.Copilot.UsageMetricsModelMetricTokenDetail), TypeInfoPropertyName = "SessionEventsUsageMetricsModelMetricTokenDetail")] +[JsonSerializable(typeof(GitHub.Copilot.UsageMetricsModelMetricUsage), TypeInfoPropertyName = "SessionEventsUsageMetricsModelMetricUsage")] +[JsonSerializable(typeof(GitHub.Copilot.UsageMetricsProviderModelMetric), TypeInfoPropertyName = "SessionEventsUsageMetricsProviderModelMetric")] +[JsonSerializable(typeof(GitHub.Copilot.UsageMetricsTokenDetail), TypeInfoPropertyName = "SessionEventsUsageMetricsTokenDetail")] [JsonSerializable(typeof(GitHub.Copilot.UserInputCompletedData), TypeInfoPropertyName = "SessionEventsUserInputCompletedData")] [JsonSerializable(typeof(GitHub.Copilot.UserInputCompletedEvent), TypeInfoPropertyName = "SessionEventsUserInputCompletedEvent")] [JsonSerializable(typeof(GitHub.Copilot.UserInputRequestedData), TypeInfoPropertyName = "SessionEventsUserInputRequestedData")] @@ -48916,13 +52565,23 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(AgentSelectRequest))] [JsonSerializable(typeof(AgentSelectResult))] [JsonSerializable(typeof(AgentSetPromptRequest))] +[JsonSerializable(typeof(AgentsCustomAgentInitialModelDecisionParams))] +[JsonSerializable(typeof(AgentsCustomAgentInitialModelDecisionResult))] [JsonSerializable(typeof(AgentsDiscoverRequest))] +[JsonSerializable(typeof(AgentsGetAvailableBuiltinsRequest))] +[JsonSerializable(typeof(AgentsGetAvailableBuiltinsResult))] +[JsonSerializable(typeof(AgentsGetBuiltinDefinitionRequest))] +[JsonSerializable(typeof(AgentsGetBuiltinDefinitionResult))] +[JsonSerializable(typeof(AgentsGetBuiltinListingDefinitionRequest))] +[JsonSerializable(typeof(AgentsGetBuiltinListingDefinitionResult))] +[JsonSerializable(typeof(AgentsGetBuiltinsResult))] [JsonSerializable(typeof(AgentsGetDiscoveryPathsRequest))] [JsonSerializable(typeof(AuthEnumerateQuery))] [JsonSerializable(typeof(AuthEnumerateValue))] [JsonSerializable(typeof(AuthIdentity))] [JsonSerializable(typeof(AuthIdentityMetadata))] [JsonSerializable(typeof(AuthInfo))] +[JsonSerializable(typeof(AuthLoginAccount))] [JsonSerializable(typeof(AuthLoginAdvanceRequest))] [JsonSerializable(typeof(AuthLoginBeginRequest))] [JsonSerializable(typeof(AuthLoginBegun))] @@ -48935,11 +52594,15 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(AuthValidationError))] [JsonSerializable(typeof(AuthWrite))] [JsonSerializable(typeof(AuthWriteResult))] +[JsonSerializable(typeof(AutoTierDescriptor))] +[JsonSerializable(typeof(AutoTierMetadata))] +[JsonSerializable(typeof(AutoTierStatus))] [JsonSerializable(typeof(AutopilotObjectiveCreditLimit))] [JsonSerializable(typeof(AutopilotObjectiveGetStateResult))] [JsonSerializable(typeof(AutopilotObjectiveState))] [JsonSerializable(typeof(BuiltInModelCatalog))] [JsonSerializable(typeof(BuiltInModelCatalogEntry))] +[JsonSerializable(typeof(BuiltinAgentSummary))] [JsonSerializable(typeof(BuiltinToolDescriptor))] [JsonSerializable(typeof(BuiltinToolFormat))] [JsonSerializable(typeof(BuiltinToolInputSchema))] @@ -49008,6 +52671,13 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ConnectorConnectResult))] [JsonSerializable(typeof(ConnectorContinueRequest))] [JsonSerializable(typeof(ConnectorDisconnectResult))] +[JsonSerializable(typeof(ConnectorDiscoveryAccount))] +[JsonSerializable(typeof(ConnectorDiscoveryAccountList))] +[JsonSerializable(typeof(ConnectorDiscoveryAccountRequest))] +[JsonSerializable(typeof(ConnectorDiscoveryAuthInfo))] +[JsonSerializable(typeof(ConnectorDiscoveryCapabilities))] +[JsonSerializable(typeof(ConnectorDiscoveryCatalogEntry))] +[JsonSerializable(typeof(ConnectorDiscoveryCatalogResult))] [JsonSerializable(typeof(ConnectorReconcileRequest))] [JsonSerializable(typeof(ConnectorReconcileRequestWithSession))] [JsonSerializable(typeof(ConnectorRuntimeStatus))] @@ -49027,6 +52697,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(CopilotUserResponseQuotaSnapshotsPremiumInteractions))] [JsonSerializable(typeof(CurrentModel))] [JsonSerializable(typeof(CurrentToolMetadata))] +[JsonSerializable(typeof(CustomizationReloadOutcome))] +[JsonSerializable(typeof(CustomizationsReloadResult))] [JsonSerializable(typeof(DebugCollectLogsCollectedEntry))] [JsonSerializable(typeof(DebugCollectLogsDestination))] [JsonSerializable(typeof(DebugCollectLogsEntry))] @@ -49048,10 +52720,10 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(DiscoveredExtensionsEnableRequest))] [JsonSerializable(typeof(DiscoveredHook))] [JsonSerializable(typeof(DiscoveredMcpServer))] +[JsonSerializable(typeof(DiscoveredModel))] +[JsonSerializable(typeof(DiscoveredModelList))] [JsonSerializable(typeof(EnqueueCommandParams))] [JsonSerializable(typeof(EnqueueCommandResult))] -[JsonSerializable(typeof(EntraTokenAcquireRequest))] -[JsonSerializable(typeof(EntraTokenAcquireResult))] [JsonSerializable(typeof(EnvironmentCapabilities))] [JsonSerializable(typeof(EnvironmentsDeleteRequest))] [JsonSerializable(typeof(EnvironmentsDeleteResult))] @@ -49080,12 +52752,29 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(FolderTrustAddParams))] [JsonSerializable(typeof(FolderTrustCheckParams))] [JsonSerializable(typeof(FolderTrustCheckResult))] +[JsonSerializable(typeof(GitCurrentBranchRemoteResult))] +[JsonSerializable(typeof(GitCwdRequest))] [JsonSerializable(typeof(GitHubEnvironment))] +[JsonSerializable(typeof(GitHubOwnerOption))] +[JsonSerializable(typeof(GitHubOwnersCancelRequest))] +[JsonSerializable(typeof(GitHubOwnersCancelResult))] +[JsonSerializable(typeof(GitHubOwnersListRequest))] +[JsonSerializable(typeof(GitHubOwnersListResult))] +[JsonSerializable(typeof(GitHubOwnersRequestIdResult))] +[JsonSerializable(typeof(GitHubRepositoryAtPathRequest))] +[JsonSerializable(typeof(GitHubRepositoryAtPathResult))] +[JsonSerializable(typeof(GitHubRepositoryIdentity))] [JsonSerializable(typeof(GitHubTelemetryClientInfo))] [JsonSerializable(typeof(GitHubTelemetryEvent))] [JsonSerializable(typeof(GitHubTelemetryNotification))] [JsonSerializable(typeof(GitHubTokenAcquireRequest))] [JsonSerializable(typeof(GitHubTokenAcquireResult))] +[JsonSerializable(typeof(GitRemoteRepository))] +[JsonSerializable(typeof(GitReposFromRemotesRequest))] +[JsonSerializable(typeof(GitReposFromRemotesResult))] +[JsonSerializable(typeof(GlobalStateLoadForConfigDirRequest))] +[JsonSerializable(typeof(GlobalStateLoadResult))] +[JsonSerializable(typeof(GlobalStateWriteKeyRequest))] [JsonSerializable(typeof(HandlePendingToolCallRequest))] [JsonSerializable(typeof(HandlePendingToolCallResult))] [JsonSerializable(typeof(HistoryAbortManualCompactionResult))] @@ -49115,6 +52804,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(HostGetConfigurationResult))] [JsonSerializable(typeof(HostGetEnvironmentCredentialsResult))] [JsonSerializable(typeof(HostGitHubEnvironmentOptions))] +[JsonSerializable(typeof(HostListSessionsRequest))] +[JsonSerializable(typeof(HostListSessionsResult))] [JsonSerializable(typeof(HostLocalServerConfiguration))] [JsonSerializable(typeof(HostLocalServerOptions))] [JsonSerializable(typeof(HostPublishSessionRequest))] @@ -49123,12 +52814,14 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(HostReadyResult))] [JsonSerializable(typeof(HostReleaseSessionRequest))] [JsonSerializable(typeof(HostReleaseSessionResult))] +[JsonSerializable(typeof(HostSessionSummary))] [JsonSerializable(typeof(HostStartRequest))] [JsonSerializable(typeof(HostStartResult))] [JsonSerializable(typeof(IDictionary))] [JsonSerializable(typeof(IDictionary))] [JsonSerializable(typeof(IList))] [JsonSerializable(typeof(IList))] +[JsonSerializable(typeof(IList))] [JsonSerializable(typeof(IList))] [JsonSerializable(typeof(IList))] [JsonSerializable(typeof(InstallationCatalogueIdentity))] @@ -49159,7 +52852,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(LocalSessionMetadataValue))] [JsonSerializable(typeof(LogRequest))] [JsonSerializable(typeof(LogResult))] +[JsonSerializable(typeof(LoggedInUser))] [JsonSerializable(typeof(LspInitializeRequest))] +[JsonSerializable(typeof(ManagedPluginRetryEntry))] [JsonSerializable(typeof(ManagedSettingMeta))] [JsonSerializable(typeof(ManagedSettingsComposeLayer))] [JsonSerializable(typeof(ManagedSettingsComposeRequest))] @@ -49210,6 +52905,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpConfigUpdateRequest))] [JsonSerializable(typeof(McpConfigureGitHubRequest))] [JsonSerializable(typeof(McpConfigureGitHubResult))] +[JsonSerializable(typeof(McpConfiguredServer))] +[JsonSerializable(typeof(McpConfiguredServerList))] +[JsonSerializable(typeof(McpConfiguredServerState))] [JsonSerializable(typeof(McpDiagnosticDetails))] [JsonSerializable(typeof(McpDiagnosticSourceConfiguration))] [JsonSerializable(typeof(McpDisableRequest))] @@ -49245,6 +52943,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpListToolsRequest))] [JsonSerializable(typeof(McpListToolsResult))] [JsonSerializable(typeof(McpOauthAuthenticationStateChangedRequest))] +[JsonSerializable(typeof(McpOauthCompleteRequest))] [JsonSerializable(typeof(McpOauthHandlePendingRequest))] [JsonSerializable(typeof(McpOauthHandlePendingResult))] [JsonSerializable(typeof(McpOauthLoginRequest))] @@ -49270,6 +52969,14 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(McpPlanUninstallRequest))] [JsonSerializable(typeof(McpPrepareInstallRequest))] [JsonSerializable(typeof(McpPreparedInstall))] +[JsonSerializable(typeof(McpPrompt))] +[JsonSerializable(typeof(McpPromptArgument))] +[JsonSerializable(typeof(McpPromptIcon))] +[JsonSerializable(typeof(McpPromptMessage))] +[JsonSerializable(typeof(McpPromptsGetRequest))] +[JsonSerializable(typeof(McpPromptsGetResult))] +[JsonSerializable(typeof(McpPromptsListRequest))] +[JsonSerializable(typeof(McpPromptsListResult))] [JsonSerializable(typeof(McpRegisterExternalClientRequest))] [JsonSerializable(typeof(McpReloadWithConfigRequest))] [JsonSerializable(typeof(McpRemoveGitHubResult))] @@ -49332,6 +53039,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ModeSetResult))] [JsonSerializable(typeof(Model))] [JsonSerializable(typeof(ModelApplyStartupOverlayRequest))] +[JsonSerializable(typeof(ModelArtifactDetails))] [JsonSerializable(typeof(ModelBilling))] [JsonSerializable(typeof(ModelBillingPromo))] [JsonSerializable(typeof(ModelBillingTokenPrices))] @@ -49350,8 +53058,24 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ModelPickerSettingsContext))] [JsonSerializable(typeof(ModelPickerSettingsContextEnvironment))] [JsonSerializable(typeof(ModelPolicy))] +[JsonSerializable(typeof(ModelProviderAdapterCatalog))] +[JsonSerializable(typeof(ModelProviderAdapterDescriptor))] +[JsonSerializable(typeof(ModelProviderAdapterOperationDescriptor))] +[JsonSerializable(typeof(ModelProviderAttribution))] +[JsonSerializable(typeof(ModelProviderAutomaticDiscoveryPolicy))] +[JsonSerializable(typeof(ModelProviderConfigurationPlan))] [JsonSerializable(typeof(ModelProviderDescriptor))] -[JsonSerializable(typeof(ModelProviderRef))] +[JsonSerializable(typeof(ModelProviderDiscoverRequest))] +[JsonSerializable(typeof(ModelProviderDiscoverResult))] +[JsonSerializable(typeof(ModelProviderGetStatusRequest))] +[JsonSerializable(typeof(ModelProviderInstance))] +[JsonSerializable(typeof(ModelProviderInstanceReference))] +[JsonSerializable(typeof(ModelProviderModelsListRequest))] +[JsonSerializable(typeof(ModelProviderOperationOutcome))] +[JsonSerializable(typeof(ModelProviderPrepareConfigurationRequest))] +[JsonSerializable(typeof(ModelProviderProvenance))] +[JsonSerializable(typeof(ModelProviderStatus))] +[JsonSerializable(typeof(ModelProviderWarning))] [JsonSerializable(typeof(ModelSetAllowedModelsRequest))] [JsonSerializable(typeof(ModelSetAllowedModelsResult))] [JsonSerializable(typeof(ModelSetReasoningEffortRequest))] @@ -49360,6 +53084,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ModelSwitchAutoTierResult))] [JsonSerializable(typeof(ModelSwitchConfirmation))] [JsonSerializable(typeof(ModelSwitchToRequest))] +[JsonSerializable(typeof(ModelSwitchToRequestWithSession))] [JsonSerializable(typeof(ModelSwitchToResult))] [JsonSerializable(typeof(ModelWarningText))] [JsonSerializable(typeof(ModelsListRequest))] @@ -49373,6 +53098,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(OptionsUpdateAdditionalContentExclusionPolicy))] [JsonSerializable(typeof(OptionsUpdateAdditionalContentExclusionPolicyRule))] [JsonSerializable(typeof(OptionsUpdateAdditionalContentExclusionPolicyRuleSource))] +[JsonSerializable(typeof(PendingExternalToolRequest))] +[JsonSerializable(typeof(PendingExternalToolRequestList))] [JsonSerializable(typeof(PendingPermissionRequest))] [JsonSerializable(typeof(PendingPermissionRequestList))] [JsonSerializable(typeof(PermissionDecision))] @@ -49514,6 +53241,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(QueueWithdrawMessageRequest))] [JsonSerializable(typeof(QueueWithdrawMessageResult))] [JsonSerializable(typeof(QueuedCommandResult))] +[JsonSerializable(typeof(QuotaWarningProjection))] [JsonSerializable(typeof(RegisterEventInterestParams))] [JsonSerializable(typeof(RegisterEventInterestResult))] [JsonSerializable(typeof(ReleaseEventInterestParams))] @@ -49549,6 +53277,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SandboxHostCapability))] [JsonSerializable(typeof(SandboxHostSupport))] [JsonSerializable(typeof(SandboxMaskedEnvVar))] +[JsonSerializable(typeof(SandboxProxyCaCreateResult))] +[JsonSerializable(typeof(SandboxProxyCaRequest))] +[JsonSerializable(typeof(SandboxProxyCaStatus))] [JsonSerializable(typeof(ScheduleAddAtRequest))] [JsonSerializable(typeof(ScheduleAddCronRequest))] [JsonSerializable(typeof(ScheduleAddRequest))] @@ -49592,6 +53323,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionCommandsListRequestWithSession))] [JsonSerializable(typeof(SessionCompletionItem))] [JsonSerializable(typeof(SessionCompletionsGetTriggerCharactersRequest))] +[JsonSerializable(typeof(SessionConnectedIdeInfo))] [JsonSerializable(typeof(SessionConnectorsGetAccountRequest))] [JsonSerializable(typeof(SessionConnectorsGetCapabilitiesRequest))] [JsonSerializable(typeof(SessionConnectorsGetStatusRequest))] @@ -49601,12 +53333,15 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionEnrichMetadataResult))] [JsonSerializable(typeof(SessionEventLogTailRequest))] [JsonSerializable(typeof(SessionExtensionsListRequest))] +[JsonSerializable(typeof(SessionExtensionsReconcileRequest))] [JsonSerializable(typeof(SessionExtensionsReloadRequest))] [JsonSerializable(typeof(SessionFsAppendFileRequest))] [JsonSerializable(typeof(SessionFsError))] [JsonSerializable(typeof(SessionFsExistsRequest))] [JsonSerializable(typeof(SessionFsExistsResult))] [JsonSerializable(typeof(SessionFsMkdirRequest))] +[JsonSerializable(typeof(SessionFsReadFileBytesRequest))] +[JsonSerializable(typeof(SessionFsReadFileBytesResult))] [JsonSerializable(typeof(SessionFsReadFileRequest))] [JsonSerializable(typeof(SessionFsReadFileResult))] [JsonSerializable(typeof(SessionFsReaddirRequest))] @@ -49629,6 +53364,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionFsSqliteTransactionStatement))] [JsonSerializable(typeof(SessionFsStatRequest))] [JsonSerializable(typeof(SessionFsStatResult))] +[JsonSerializable(typeof(SessionFsWriteFileBytesRequest))] [JsonSerializable(typeof(SessionFsWriteFileRequest))] [JsonSerializable(typeof(SessionGitHubAuthGetAllAuthAvailableRequest))] [JsonSerializable(typeof(SessionGitHubAuthGetCurrentAuthInfoRequest))] @@ -49657,6 +53393,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionLoadDeferredRepoHooksResult))] [JsonSerializable(typeof(SessionManagedSettingsGetRequest))] [JsonSerializable(typeof(SessionMcpAppsGetHostContextRequest))] +[JsonSerializable(typeof(SessionMcpListConfiguredRequest))] [JsonSerializable(typeof(SessionMcpListRequest))] [JsonSerializable(typeof(SessionMcpMoveLoadingToBackgroundRequest))] [JsonSerializable(typeof(SessionMcpOauthCancelLoginRequest))] @@ -49665,6 +53402,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionMcpOauthPrepareLoginResult))] [JsonSerializable(typeof(SessionMcpReloadRequest))] [JsonSerializable(typeof(SessionMcpRemoveGitHubRequest))] +[JsonSerializable(typeof(SessionMcpSetConnectedIdeInfoParams))] [JsonSerializable(typeof(SessionMetadataActivityRequest))] [JsonSerializable(typeof(SessionMetadataGetClientMetadataRequest))] [JsonSerializable(typeof(SessionMetadataGetContextAttributionRequest))] @@ -49673,6 +53411,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionMetadataSnapshotRequest))] [JsonSerializable(typeof(SessionMetadataSnapshotWorkspace))] [JsonSerializable(typeof(SessionModeGetRequest))] +[JsonSerializable(typeof(SessionModelClearStartupSeedRequest))] +[JsonSerializable(typeof(SessionModelClearStartupSeedResult))] [JsonSerializable(typeof(SessionModelGetCurrentRequest))] [JsonSerializable(typeof(SessionModelList))] [JsonSerializable(typeof(SessionModelListRequest))] @@ -49693,8 +53433,12 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionPluginsMarketplacesRefreshRequestWithSession))] [JsonSerializable(typeof(SessionPluginsReloadRequest))] [JsonSerializable(typeof(SessionPluginsReloadRequestWithSession))] +[JsonSerializable(typeof(SessionPluginsRetryManagedRequest))] +[JsonSerializable(typeof(SessionPluginsRetryManagedRequestWithSession))] +[JsonSerializable(typeof(SessionPluginsRetryManagedResult))] [JsonSerializable(typeof(SessionProviderGetEndpointRequest))] [JsonSerializable(typeof(SessionProviderGetEndpointRequestWithSession))] +[JsonSerializable(typeof(SessionProvidersGetCatalogRequest))] [JsonSerializable(typeof(SessionPruneResult))] [JsonSerializable(typeof(SessionQueueClearRequest))] [JsonSerializable(typeof(SessionQueueEnqueueResumePendingRequest))] @@ -49703,6 +53447,13 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionQueueProcessRequest))] [JsonSerializable(typeof(SessionQueueRemoveMostRecentRequest))] [JsonSerializable(typeof(SessionQueueSnapshotRequest))] +[JsonSerializable(typeof(SessionQuotaDelegateWarning))] +[JsonSerializable(typeof(SessionQuotaGetRequest))] +[JsonSerializable(typeof(SessionQuotaGetResult))] +[JsonSerializable(typeof(SessionQuotaRefreshRequest))] +[JsonSerializable(typeof(SessionQuotaRefreshResult))] +[JsonSerializable(typeof(SessionQuotaSnapshot))] +[JsonSerializable(typeof(SessionQuotaTakeWarningsRequest))] [JsonSerializable(typeof(SessionRemoteDisableRequest))] [JsonSerializable(typeof(SessionSandboxGetEnforcementStatusRequest))] [JsonSerializable(typeof(SessionScheduleHasSelfPacedRequest))] @@ -49739,9 +53490,11 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionUpdateOptionsParams))] [JsonSerializable(typeof(SessionUpdateOptionsResult))] [JsonSerializable(typeof(SessionUsageGetMetricsRequest))] +[JsonSerializable(typeof(SessionUsageSetCodeChangesRequest))] [JsonSerializable(typeof(SessionVisibilityGetRequest))] [JsonSerializable(typeof(SessionWorkflowPauseAtCheckpointResult))] [JsonSerializable(typeof(SessionWorkingDirectoryContext))] +[JsonSerializable(typeof(SessionWorkingDirectoryContextWithClient))] [JsonSerializable(typeof(SessionWorkspacesAutopilotObjectiveExistsRequest))] [JsonSerializable(typeof(SessionWorkspacesDeleteAutopilotObjectiveRequest))] [JsonSerializable(typeof(SessionWorkspacesGetWorkspaceRequest))] @@ -49754,6 +53507,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionsClientMetadataEntry))] [JsonSerializable(typeof(SessionsCloseRequest))] [JsonSerializable(typeof(SessionsCloseResult))] +[JsonSerializable(typeof(SessionsCreateWorkspaceRequest))] +[JsonSerializable(typeof(SessionsCreateWorkspaceResult))] [JsonSerializable(typeof(SessionsDeleteRequest))] [JsonSerializable(typeof(SessionsEnrichMetadataRequest))] [JsonSerializable(typeof(SessionsFindByPrefixRequest))] @@ -49777,6 +53532,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionsListNonEmptySessionIdsResult))] [JsonSerializable(typeof(SessionsListRequest))] [JsonSerializable(typeof(SessionsLoadDeferredRepoHooksRequest))] +[JsonSerializable(typeof(SessionsLoadWorkspaceRequest))] +[JsonSerializable(typeof(SessionsLoadWorkspaceResult))] [JsonSerializable(typeof(SessionsOpenProgress))] [JsonSerializable(typeof(SessionsPruneOldRequest))] [JsonSerializable(typeof(SessionsReadPersistedEventsRequest))] @@ -49792,6 +53549,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(SessionsStartRemoteControlRequest))] [JsonSerializable(typeof(SessionsStopRemoteControlRequest))] [JsonSerializable(typeof(SessionsTransferRemoteControlRequest))] +[JsonSerializable(typeof(SessionsUpdateWorkspaceFieldsRequest))] +[JsonSerializable(typeof(SessionsUpdateWorkspaceFieldsResult))] [JsonSerializable(typeof(SettableAuthInfo))] [JsonSerializable(typeof(ShellCancelUserRequestedRequest))] [JsonSerializable(typeof(ShellCredentials))] @@ -49884,6 +53643,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(ToolsGetBuiltinDescriptorsResult))] [JsonSerializable(typeof(ToolsGetCurrentMetadataResult))] [JsonSerializable(typeof(ToolsInitializeAndValidateResult))] +[JsonSerializable(typeof(ToolsListPendingRequestsRequest))] [JsonSerializable(typeof(ToolsListRequest))] [JsonSerializable(typeof(ToolsSetRequest))] [JsonSerializable(typeof(ToolsSetResult))] @@ -49912,19 +53672,10 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(UIUserInputResponse))] [JsonSerializable(typeof(UpdateSubagentSettingsRequest))] [JsonSerializable(typeof(UpdateSubagentSettingsRequestSubagents))] -[JsonSerializable(typeof(UsageGetMetricsResult))] -[JsonSerializable(typeof(UsageMetricsAgentMetric))] -[JsonSerializable(typeof(UsageMetricsCodeChanges))] -[JsonSerializable(typeof(UsageMetricsModelMetric))] -[JsonSerializable(typeof(UsageMetricsModelMetricRequests))] -[JsonSerializable(typeof(UsageMetricsModelMetricTokenDetail))] -[JsonSerializable(typeof(UsageMetricsModelMetricUsage))] -[JsonSerializable(typeof(UsageMetricsTokenDetail))] [JsonSerializable(typeof(UserRequestedShellCommandResult))] [JsonSerializable(typeof(UserSettingMetadata))] [JsonSerializable(typeof(UserSettingsGetResult))] [JsonSerializable(typeof(UserSettingsSetRequest))] -[JsonSerializable(typeof(UserSettingsSetResult))] [JsonSerializable(typeof(VisibilityGetResult))] [JsonSerializable(typeof(VisibilitySetRequest))] [JsonSerializable(typeof(VisibilitySetResult))] @@ -49968,6 +53719,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(WorkflowToolResumeRequest))] [JsonSerializable(typeof(WorkflowToolRunOptions))] [JsonSerializable(typeof(WorkflowToolRunRequest))] +[JsonSerializable(typeof(WorkspaceDiffContents))] [JsonSerializable(typeof(WorkspaceDiffFileChange))] [JsonSerializable(typeof(WorkspaceDiffResult))] [JsonSerializable(typeof(WorkspacesAddSummaryRequest))] @@ -49980,6 +53732,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH [JsonSerializable(typeof(WorkspacesCreateFileRequest))] [JsonSerializable(typeof(WorkspacesDeleteAutopilotObjectiveResult))] [JsonSerializable(typeof(WorkspacesDiffRequest))] +[JsonSerializable(typeof(WorkspacesDiffRequestWithSession))] [JsonSerializable(typeof(WorkspacesEnsureRequest))] [JsonSerializable(typeof(WorkspacesGetWorkspaceResult))] [JsonSerializable(typeof(WorkspacesGetWorkspaceResultWorkspace))] diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs index 29cd2d23fa..fb23ccb58a 100644 --- a/dotnet/src/Generated/SessionEvents.cs +++ b/dotnet/src/Generated/SessionEvents.cs @@ -14,6 +14,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; namespace GitHub.Copilot; @@ -60,6 +61,7 @@ namespace GitHub.Copilot; [JsonDerivedType(typeof(HookEndEvent), "hook.end")] [JsonDerivedType(typeof(HookProgressEvent), "hook.progress")] [JsonDerivedType(typeof(HookStartEvent), "hook.start")] +[JsonDerivedType(typeof(HumanResponseRecordedEvent), "human_response.recorded")] [JsonDerivedType(typeof(McpAppToolCallCompleteEvent), "mcp_app.tool_call_complete")] [JsonDerivedType(typeof(McpHeadersRefreshCompletedEvent), "mcp.headers_refresh_completed")] [JsonDerivedType(typeof(McpHeadersRefreshRequiredEvent), "mcp.headers_refresh_required")] @@ -117,6 +119,7 @@ namespace GitHub.Copilot; [JsonDerivedType(typeof(SessionIdleEvent), "session.idle")] [JsonDerivedType(typeof(SessionIndexedSearchEvent), "session.indexed_search")] [JsonDerivedType(typeof(SessionInfoEvent), "session.info")] +[JsonDerivedType(typeof(SessionManagedPluginProgressEvent), "session.managed_plugin_progress")] [JsonDerivedType(typeof(SessionManagedSettingsEnforcedEvent), "session.managed_settings_enforced")] [JsonDerivedType(typeof(SessionManagedSettingsResolvedEvent), "session.managed_settings_resolved")] [JsonDerivedType(typeof(SessionMcpServerNeedsReconnectEvent), "session.mcp_server_needs_reconnect")] @@ -130,6 +133,7 @@ namespace GitHub.Copilot; [JsonDerivedType(typeof(SessionPermissionRecoveryEvent), "session.permission_recovery")] [JsonDerivedType(typeof(SessionPermissionsChangedEvent), "session.permissions_changed")] [JsonDerivedType(typeof(SessionPlanChangedEvent), "session.plan_changed")] +[JsonDerivedType(typeof(SessionQuotaObservationEvent), "session.quota_observation")] [JsonDerivedType(typeof(SessionRemoteSteerableChangedEvent), "session.remote_steerable_changed")] [JsonDerivedType(typeof(SessionResumeEvent), "session.resume")] [JsonDerivedType(typeof(SessionScheduleCancelledEvent), "session.schedule_cancelled")] @@ -166,6 +170,7 @@ namespace GitHub.Copilot; [JsonDerivedType(typeof(ToolExecutionPartialResultEvent), "tool.execution_partial_result")] [JsonDerivedType(typeof(ToolExecutionProgressEvent), "tool.execution_progress")] [JsonDerivedType(typeof(ToolExecutionStartEvent), "tool.execution_start")] +[JsonDerivedType(typeof(ToolShellOutputEvent), "tool.shell_output")] [JsonDerivedType(typeof(ToolUserRequestedEvent), "tool.user_requested")] [JsonDerivedType(typeof(UiEphemeralQueryEvent), "ui.ephemeral_query")] [JsonDerivedType(typeof(UserInputCompletedEvent), "user_input.completed")] @@ -206,7 +211,7 @@ public partial class SessionEvent /// Deserializes a JSON string into a . public static SessionEvent FromJson(string json) => - JsonSerializer.Deserialize(json, SessionEventsJsonContext.Default.SessionEvent)!; + SessionEventJsonConverter.Deserialize(json); /// Serializes this event to a JSON string. public string ToJson() => @@ -216,6 +221,523 @@ public string ToJson() => private string DebuggerDisplay => ToJson(); } +internal sealed partial class SessionEventJsonConverter : JsonConverter +{ + internal static SessionEventJsonConverter Default { get; } = new(); + + public override SessionEvent? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + // Preserve the reader's position for deserialization. + var probe = reader; + JsonTypeInfo typeInfo = ReadEventTypeInfo(ref probe); + return ToSessionEvent(JsonSerializer.Deserialize(ref reader, typeInfo)); + } + + private static JsonTypeInfo? GetEventTypeInfo(ReadOnlySpan type) + { + switch (type.Length) + { + case 5: + if (type.SequenceEqual("abort"u8)) + return SessionEventsJsonContext.Default.AbortEvent; + break; + case 8: + if (type.SequenceEqual("hook.end"u8)) + return SessionEventsJsonContext.Default.HookEndEvent; + break; + case 10: + if (type.SequenceEqual("hook.start"u8)) + return SessionEventsJsonContext.Default.HookStartEvent; + break; + case 12: + if (type.SequenceEqual("session.idle"u8)) + return SessionEventsJsonContext.Default.SessionIdleEvent; + if (type.SequenceEqual("session.info"u8)) + return SessionEventsJsonContext.Default.SessionInfoEvent; + if (type.SequenceEqual("user.message"u8)) + return SessionEventsJsonContext.Default.UserMessageEvent; + break; + case 13: + if (type.SequenceEqual("hook.progress"u8)) + return SessionEventsJsonContext.Default.HookProgressEvent; + if (type.SequenceEqual("session.error"u8)) + return SessionEventsJsonContext.Default.SessionErrorEvent; + if (type.SequenceEqual("session.start"u8)) + return SessionEventsJsonContext.Default.SessionStartEvent; + if (type.SequenceEqual("skill.invoked"u8)) + return SessionEventsJsonContext.Default.SkillInvokedEvent; + break; + case 14: + if (type.SequenceEqual("assistant.idle"u8)) + return SessionEventsJsonContext.Default.AssistantIdleEvent; + if (type.SequenceEqual("command.queued"u8)) + return SessionEventsJsonContext.Default.CommandQueuedEvent; + if (type.SequenceEqual("session.resume"u8)) + return SessionEventsJsonContext.Default.SessionResumeEvent; + if (type.SequenceEqual("system.message"u8)) + return SessionEventsJsonContext.Default.SystemMessageEvent; + break; + case 15: + if (type.SequenceEqual("assistant.usage"u8)) + return SessionEventsJsonContext.Default.AssistantUsageEvent; + if (type.SequenceEqual("command.execute"u8)) + return SessionEventsJsonContext.Default.CommandExecuteEvent; + if (type.SequenceEqual("session.handoff"u8)) + return SessionEventsJsonContext.Default.SessionHandoffEvent; + if (type.SequenceEqual("session.warning"u8)) + return SessionEventsJsonContext.Default.SessionWarningEvent; + if (type.SequenceEqual("subagent.failed"u8)) + return SessionEventsJsonContext.Default.SubagentFailedEvent; + break; + case 16: + if (type.SequenceEqual("assistant.intent"u8)) + return SessionEventsJsonContext.Default.AssistantIntentEvent; + if (type.SequenceEqual("commands.changed"u8)) + return SessionEventsJsonContext.Default.CommandsChangedEvent; + if (type.SequenceEqual("model.call_start"u8)) + return SessionEventsJsonContext.Default.ModelCallStartEvent; + if (type.SequenceEqual("sandbox.decision"u8)) + return SessionEventsJsonContext.Default.SandboxDecisionEvent; + if (type.SequenceEqual("session.shutdown"u8)) + return SessionEventsJsonContext.Default.SessionShutdownEvent; + if (type.SequenceEqual("subagent.started"u8)) + return SessionEventsJsonContext.Default.SubagentStartedEvent; + break; + case 17: + if (type.SequenceEqual("agent.interrupted"u8)) + return SessionEventsJsonContext.Default.AgentInterruptedEvent; + if (type.SequenceEqual("assistant.message"u8)) + return SessionEventsJsonContext.Default.AssistantMessageEvent; + if (type.SequenceEqual("command.completed"u8)) + return SessionEventsJsonContext.Default.CommandCompletedEvent; + if (type.SequenceEqual("skill.invoked_ref"u8)) + return SessionEventsJsonContext.Default.SkillInvokedRefEvent; + if (type.SequenceEqual("subagent.selected"u8)) + return SessionEventsJsonContext.Default.SubagentSelectedEvent; + if (type.SequenceEqual("tool.shell_output"u8)) + return SessionEventsJsonContext.Default.ToolShellOutputEvent; + break; + case 18: + if (type.SequenceEqual("assistant.turn_end"u8)) + return SessionEventsJsonContext.Default.AssistantTurnEndEvent; + if (type.SequenceEqual("mcp.oauth_required"u8)) + return SessionEventsJsonContext.Default.McpOauthRequiredEvent; + if (type.SequenceEqual("model.call_failure"u8)) + return SessionEventsJsonContext.Default.ModelCallFailureEvent; + if (type.SequenceEqual("prompt_cache_break"u8)) + return SessionEventsJsonContext.Default.PromptCacheBreakEvent; + if (type.SequenceEqual("sampling.completed"u8)) + return SessionEventsJsonContext.Default.SamplingCompletedEvent; + if (type.SequenceEqual("sampling.requested"u8)) + return SessionEventsJsonContext.Default.SamplingRequestedEvent; + if (type.SequenceEqual("session.truncation"u8)) + return SessionEventsJsonContext.Default.SessionTruncationEvent; + if (type.SequenceEqual("session.usage_info"u8)) + return SessionEventsJsonContext.Default.SessionUsageInfoEvent; + if (type.SequenceEqual("subagent.completed"u8)) + return SessionEventsJsonContext.Default.SubagentCompletedEvent; + if (type.SequenceEqual("ui.ephemeral_query"u8)) + return SessionEventsJsonContext.Default.UiEphemeralQueryEvent; + break; + case 19: + if (type.SequenceEqual("assistant.reasoning"u8)) + return SessionEventsJsonContext.Default.AssistantReasoningEvent; + if (type.SequenceEqual("mcp.oauth_completed"u8)) + return SessionEventsJsonContext.Default.McpOauthCompletedEvent; + if (type.SequenceEqual("model.call_finished"u8)) + return SessionEventsJsonContext.Default.ModelCallFinishedEvent; + if (type.SequenceEqual("subagent.configured"u8)) + return SessionEventsJsonContext.Default.SubagentConfiguredEvent; + if (type.SequenceEqual("subagent.deselected"u8)) + return SessionEventsJsonContext.Default.SubagentDeselectedEvent; + if (type.SequenceEqual("system.notification"u8)) + return SessionEventsJsonContext.Default.SystemNotificationEvent; + if (type.SequenceEqual("tool.user_requested"u8)) + return SessionEventsJsonContext.Default.ToolUserRequestedEvent; + break; + case 20: + if (type.SequenceEqual("assistant.turn_retry"u8)) + return SessionEventsJsonContext.Default.AssistantTurnRetryEvent; + if (type.SequenceEqual("assistant.turn_start"u8)) + return SessionEventsJsonContext.Default.AssistantTurnStartEvent; + if (type.SequenceEqual("capabilities.changed"u8)) + return SessionEventsJsonContext.Default.CapabilitiesChangedEvent; + if (type.SequenceEqual("permission.completed"u8)) + return SessionEventsJsonContext.Default.PermissionCompletedEvent; + if (type.SequenceEqual("permission.requested"u8)) + return SessionEventsJsonContext.Default.PermissionRequestedEvent; + if (type.SequenceEqual("session.binary_asset"u8)) + return SessionEventsJsonContext.Default.SessionBinaryAssetEvent; + if (type.SequenceEqual("session.mode_changed"u8)) + return SessionEventsJsonContext.Default.SessionModeChangedEvent; + if (type.SequenceEqual("session.model_change"u8)) + return SessionEventsJsonContext.Default.SessionModelChangeEvent; + if (type.SequenceEqual("session.plan_changed"u8)) + return SessionEventsJsonContext.Default.SessionPlanChangedEvent; + if (type.SequenceEqual("tool.execution_start"u8)) + return SessionEventsJsonContext.Default.ToolExecutionStartEvent; + if (type.SequenceEqual("user_input.completed"u8)) + return SessionEventsJsonContext.Default.UserInputCompletedEvent; + if (type.SequenceEqual("user_input.requested"u8)) + return SessionEventsJsonContext.Default.UserInputRequestedEvent; + if (type.SequenceEqual("workflow.run_settled"u8)) + return SessionEventsJsonContext.Default.WorkflowRunSettledEvent; + if (type.SequenceEqual("workflow.run_started"u8)) + return SessionEventsJsonContext.Default.WorkflowRunStartedEvent; + if (type.SequenceEqual("workflow.run_updated"u8)) + return SessionEventsJsonContext.Default.WorkflowRunUpdatedEvent; + break; + case 21: + if (type.SequenceEqual("elicitation.completed"u8)) + return SessionEventsJsonContext.Default.ElicitationCompletedEvent; + if (type.SequenceEqual("elicitation.requested"u8)) + return SessionEventsJsonContext.Default.ElicitationRequestedEvent; + if (type.SequenceEqual("session.canvas.closed"u8)) + return SessionEventsJsonContext.Default.SessionCanvasClosedEvent; + if (type.SequenceEqual("session.canvas.opened"u8)) + return SessionEventsJsonContext.Default.SessionCanvasOpenedEvent; + if (type.SequenceEqual("session.skills_loaded"u8)) + return SessionEventsJsonContext.Default.SessionSkillsLoadedEvent; + if (type.SequenceEqual("session.task_complete"u8)) + return SessionEventsJsonContext.Default.SessionTaskCompleteEvent; + if (type.SequenceEqual("session.title_changed"u8)) + return SessionEventsJsonContext.Default.SessionTitleChangedEvent; + if (type.SequenceEqual("session.todos_changed"u8)) + return SessionEventsJsonContext.Default.SessionTodosChangedEvent; + if (type.SequenceEqual("session.tools_updated"u8)) + return SessionEventsJsonContext.Default.SessionToolsUpdatedEvent; + if (type.SequenceEqual("tool_search.activated"u8)) + return SessionEventsJsonContext.Default.ToolSearchActivatedEvent; + break; + case 22: + if (type.SequenceEqual("mcp.tools.list_changed"u8)) + return SessionEventsJsonContext.Default.McpToolsListChangedEvent; + if (type.SequenceEqual("session.canvas.removed"u8)) + return SessionEventsJsonContext.Default.SessionCanvasRemovedEvent; + if (type.SequenceEqual("session.indexed_search"u8)) + return SessionEventsJsonContext.Default.SessionIndexedSearchEvent; + break; + case 23: + if (type.SequenceEqual("assistant.message_delta"u8)) + return SessionEventsJsonContext.Default.AssistantMessageDeltaEvent; + if (type.SequenceEqual("assistant.message_start"u8)) + return SessionEventsJsonContext.Default.AssistantMessageStartEvent; + if (type.SequenceEqual("external_tool.completed"u8)) + return SessionEventsJsonContext.Default.ExternalToolCompletedEvent; + if (type.SequenceEqual("external_tool.requested"u8)) + return SessionEventsJsonContext.Default.ExternalToolRequestedEvent; + if (type.SequenceEqual("human_response.recorded"u8)) + return SessionEventsJsonContext.Default.HumanResponseRecordedEvent; + if (type.SequenceEqual("model.call_final_result"u8)) + return SessionEventsJsonContext.Default.ModelCallFinalResultEvent; + if (type.SequenceEqual("session.canvas.recorded"u8)) + return SessionEventsJsonContext.Default.SessionCanvasRecordedEvent; + if (type.SequenceEqual("session.context_changed"u8)) + return SessionEventsJsonContext.Default.SessionContextChangedEvent; + if (type.SequenceEqual("session.context_cleared"u8)) + return SessionEventsJsonContext.Default.SessionContextClearedEvent; + if (type.SequenceEqual("session.fusion_resolved"u8)) + return SessionEventsJsonContext.Default.SessionFusionResolvedEvent; + if (type.SequenceEqual("session.snapshot_rewind"u8)) + return SessionEventsJsonContext.Default.SessionSnapshotRewindEvent; + if (type.SequenceEqual("skill.context_delivered"u8)) + return SessionEventsJsonContext.Default.SkillContextDeliveredEvent; + if (type.SequenceEqual("tool.execution_complete"u8)) + return SessionEventsJsonContext.Default.ToolExecutionCompleteEvent; + if (type.SequenceEqual("tool.execution_progress"u8)) + return SessionEventsJsonContext.Default.ToolExecutionProgressEvent; + break; + case 24: + if (type.SequenceEqual("exit_plan_mode.completed"u8)) + return SessionEventsJsonContext.Default.ExitPlanModeCompletedEvent; + if (type.SequenceEqual("exit_plan_mode.requested"u8)) + return SessionEventsJsonContext.Default.ExitPlanModeRequestedEvent; + if (type.SequenceEqual("mcp.prompts.list_changed"u8)) + return SessionEventsJsonContext.Default.McpPromptsListChangedEvent; + if (type.SequenceEqual("session.compaction_start"u8)) + return SessionEventsJsonContext.Default.SessionCompactionStartEvent; + if (type.SequenceEqual("session.fusion_completed"u8)) + return SessionEventsJsonContext.Default.SessionFusionCompletedEvent; + if (type.SequenceEqual("session.model_deselected"u8)) + return SessionEventsJsonContext.Default.SessionModelDeselectedEvent; + if (type.SequenceEqual("session.schedule_created"u8)) + return SessionEventsJsonContext.Default.SessionScheduleCreatedEvent; + if (type.SequenceEqual("session.schedule_rearmed"u8)) + return SessionEventsJsonContext.Default.SessionScheduleRearmedEvent; + if (type.SequenceEqual("session.usage_checkpoint"u8)) + return SessionEventsJsonContext.Default.SessionUsageCheckpointEvent; + break; + case 25: + if (type.SequenceEqual("assistant.reasoning_delta"u8)) + return SessionEventsJsonContext.Default.AssistantReasoningDeltaEvent; + if (type.SequenceEqual("assistant.streaming_delta"u8)) + return SessionEventsJsonContext.Default.AssistantStreamingDeltaEvent; + if (type.SequenceEqual("assistant.tool_call_delta"u8)) + return SessionEventsJsonContext.Default.AssistantToolCallDeltaEvent; + if (type.SequenceEqual("pending_messages.modified"u8)) + return SessionEventsJsonContext.Default.PendingMessagesModifiedEvent; + if (type.SequenceEqual("permission.assentDetected"u8)) + return SessionEventsJsonContext.Default.PermissionAssentDetectedEvent; + if (type.SequenceEqual("permission.carriedForward"u8)) + return SessionEventsJsonContext.Default.PermissionCarriedForwardEvent; + if (type.SequenceEqual("session.extensions_loaded"u8)) + return SessionEventsJsonContext.Default.SessionExtensionsLoadedEvent; + if (type.SequenceEqual("session.quota_observation"u8)) + return SessionEventsJsonContext.Default.SessionQuotaObservationEvent; + break; + case 26: + if (type.SequenceEqual("auto_mode_switch.completed"u8)) + return SessionEventsJsonContext.Default.AutoModeSwitchCompletedEvent; + if (type.SequenceEqual("auto_mode_switch.requested"u8)) + return SessionEventsJsonContext.Default.AutoModeSwitchRequestedEvent; + if (type.SequenceEqual("mcp_app.tool_call_complete"u8)) + return SessionEventsJsonContext.Default.McpAppToolCallCompleteEvent; + if (type.SequenceEqual("mcp.resources.list_changed"u8)) + return SessionEventsJsonContext.Default.McpResourcesListChangedEvent; + if (type.SequenceEqual("session.auto_mode_resolved"u8)) + return SessionEventsJsonContext.Default.SessionAutoModeResolvedEvent; + if (type.SequenceEqual("session.canvas.unavailable"u8)) + return SessionEventsJsonContext.Default.SessionCanvasUnavailableEvent; + if (type.SequenceEqual("session.completion_receipt"u8)) + return SessionEventsJsonContext.Default.SessionCompletionReceiptEvent; + if (type.SequenceEqual("session.mcp_server_removed"u8)) + return SessionEventsJsonContext.Default.SessionMcpServerRemovedEvent; + if (type.SequenceEqual("session.mcp_servers_loaded"u8)) + return SessionEventsJsonContext.Default.SessionMcpServersLoadedEvent; + if (type.SequenceEqual("session.schedule_cancelled"u8)) + return SessionEventsJsonContext.Default.SessionScheduleCancelledEvent; + break; + case 27: + if (type.SequenceEqual("session.compaction_complete"u8)) + return SessionEventsJsonContext.Default.SessionCompactionCompleteEvent; + if (type.SequenceEqual("session.custom_notification"u8)) + return SessionEventsJsonContext.Default.SessionCustomNotificationEvent; + if (type.SequenceEqual("session.fusion_route_failed"u8)) + return SessionEventsJsonContext.Default.SessionFusionRouteFailedEvent; + if (type.SequenceEqual("session.permission_recovery"u8)) + return SessionEventsJsonContext.Default.SessionPermissionRecoveryEvent; + if (type.SequenceEqual("session.permissions_changed"u8)) + return SessionEventsJsonContext.Default.SessionPermissionsChangedEvent; + if (type.SequenceEqual("skill.context_delivered_ref"u8)) + return SessionEventsJsonContext.Default.SkillContextDeliveredRefEvent; + break; + case 28: + if (type.SequenceEqual("mcp.headers_refresh_required"u8)) + return SessionEventsJsonContext.Default.McpHeadersRefreshRequiredEvent; + if (type.SequenceEqual("session.fusion_route_started"u8)) + return SessionEventsJsonContext.Default.SessionFusionRouteStartedEvent; + break; + case 29: + if (type.SequenceEqual("assistant.fusion_phase_failed"u8)) + return SessionEventsJsonContext.Default.AssistantFusionPhaseFailedEvent; + if (type.SequenceEqual("mcp.headers_refresh_completed"u8)) + return SessionEventsJsonContext.Default.McpHeadersRefreshCompletedEvent; + if (type.SequenceEqual("session.custom_agents_updated"u8)) + return SessionEventsJsonContext.Default.SessionCustomAgentsUpdatedEvent; + if (type.SequenceEqual("session.mode_notice_delivered"u8)) + return SessionEventsJsonContext.Default.SessionModeNoticeDeliveredEvent; + if (type.SequenceEqual("tool.execution_partial_result"u8)) + return SessionEventsJsonContext.Default.ToolExecutionPartialResultEvent; + break; + case 30: + if (type.SequenceEqual("assistant.fusion_phase_started"u8)) + return SessionEventsJsonContext.Default.AssistantFusionPhaseStartedEvent; + if (type.SequenceEqual("assistant.server_tool_progress"u8)) + return SessionEventsJsonContext.Default.AssistantServerToolProgressEvent; + if (type.SequenceEqual("session.session_limits_changed"u8)) + return SessionEventsJsonContext.Default.SessionSessionLimitsChangedEvent; + if (type.SequenceEqual("session.workspace_file_changed"u8)) + return SessionEventsJsonContext.Default.SessionWorkspaceFileChangedEvent; + break; + case 31: + if (type.SequenceEqual("assistant.fusion_phase_activity"u8)) + return SessionEventsJsonContext.Default.AssistantFusionPhaseActivityEvent; + if (type.SequenceEqual("permission.messageAuthorization"u8)) + return SessionEventsJsonContext.Default.PermissionMessageAuthorizationEvent; + if (type.SequenceEqual("session.auto_tier_switch_failed"u8)) + return SessionEventsJsonContext.Default.SessionAutoTierSwitchFailedEvent; + if (type.SequenceEqual("session.canvas.registry_changed"u8)) + return SessionEventsJsonContext.Default.SessionCanvasRegistryChangedEvent; + if (type.SequenceEqual("session.managed_plugin_progress"u8)) + return SessionEventsJsonContext.Default.SessionManagedPluginProgressEvent; + break; + case 32: + if (type.SequenceEqual("assistant.fusion_phase_completed"u8)) + return SessionEventsJsonContext.Default.AssistantFusionPhaseCompletedEvent; + if (type.SequenceEqual("session.auto_tier_recommendation"u8)) + return SessionEventsJsonContext.Default.SessionAutoTierRecommendationEvent; + if (type.SequenceEqual("session.background_tasks_changed"u8)) + return SessionEventsJsonContext.Default.SessionBackgroundTasksChangedEvent; + if (type.SequenceEqual("session.remote_steerable_changed"u8)) + return SessionEventsJsonContext.Default.SessionRemoteSteerableChangedEvent; + break; + case 33: + if (type.SequenceEqual("session.managed_settings_enforced"u8)) + return SessionEventsJsonContext.Default.SessionManagedSettingsEnforcedEvent; + if (type.SequenceEqual("session.managed_settings_resolved"u8)) + return SessionEventsJsonContext.Default.SessionManagedSettingsResolvedEvent; + if (type.SequenceEqual("session.mcp_server_status_changed"u8)) + return SessionEventsJsonContext.Default.SessionMcpServerStatusChangedEvent; + break; + case 34: + if (type.SequenceEqual("permission.contextualAuthorization"u8)) + return SessionEventsJsonContext.Default.PermissionContextualAuthorizationEvent; + if (type.SequenceEqual("session_limits_exhausted.completed"u8)) + return SessionEventsJsonContext.Default.SessionLimitsExhaustedCompletedEvent; + if (type.SequenceEqual("session_limits_exhausted.requested"u8)) + return SessionEventsJsonContext.Default.SessionLimitsExhaustedRequestedEvent; + if (type.SequenceEqual("session.mcp_server_needs_reconnect"u8)) + return SessionEventsJsonContext.Default.SessionMcpServerNeedsReconnectEvent; + break; + case 35: + if (type.SequenceEqual("permission.messageAuthorizationRead"u8)) + return SessionEventsJsonContext.Default.PermissionMessageAuthorizationReadEvent; + if (type.SequenceEqual("session.autopilot_objective_changed"u8)) + return SessionEventsJsonContext.Default.SessionAutopilotObjectiveChangedEvent; + break; + case 37: + if (type.SequenceEqual("session.extensions.attachments_pushed"u8)) + return SessionEventsJsonContext.Default.SessionExtensionsAttachmentsPushedEvent; + break; + case 39: + if (type.SequenceEqual("permission.messageAuthorizationDegraded"u8)) + return SessionEventsJsonContext.Default.PermissionMessageAuthorizationDegradedEvent; + break; + } + return null; + } + + public override void Write( + Utf8JsonWriter writer, + SessionEvent value, + JsonSerializerOptions options) => + JsonSerializer.Serialize(writer, value, SessionEventsJsonContext.Default.SessionEvent); + + private static SessionEvent ToSessionEvent(object? value) => + value as SessionEvent ?? ((SessionEventEnvelope?)value)?.ToSessionEvent()!; + + private static JsonTypeInfo ReadEventTypeInfo(ref Utf8JsonReader reader) + { + if (reader.TokenType == JsonTokenType.Null) + { + return SessionEventsJsonContext.Default.SessionEventEnvelope; + } + + if (reader.TokenType != JsonTokenType.StartObject) + { + throw new JsonException("Expected a session event object."); + } + + JsonTypeInfo? typeInfo = null; + bool foundDiscriminator = false; + while (reader.Read() && reader.TokenType == JsonTokenType.PropertyName) + { + bool isDiscriminator = reader.ValueIsEscaped + ? ReadString(ref reader) == "type" + : reader.ValueTextEquals("type"u8); + if (isDiscriminator) + { + if (foundDiscriminator) + { + throw new JsonException("Duplicate session event type discriminator."); + } + foundDiscriminator = true; + reader.Read(); + if (reader.TokenType == JsonTokenType.String) + { + if (reader.HasValueSequence || reader.ValueIsEscaped) + { + typeInfo = GetEventTypeInfo(StrictUtf8.GetBytes(ReadString(ref reader))); + } + else + { + typeInfo = GetEventTypeInfo(reader.ValueSpan); + if (typeInfo is null) + { + // Validate unknown discriminator text too, including invalid UTF-8. + _ = ReadString(ref reader); + } + } + } + else if (reader.TokenType != JsonTokenType.Number || !reader.TryGetInt32(out _)) + { + throw new JsonException("Expected a string or integer session event type discriminator."); + } + } + else + { + bool isMetadata = reader.HasValueSequence || reader.ValueIsEscaped + ? ReadString(ref reader) is { Length: > 0 } propertyName && propertyName[0] == '$' + : !reader.ValueSpan.IsEmpty && reader.ValueSpan[0] == (byte)'$'; + if (isMetadata) + { + throw new JsonException("Unexpected session event metadata property."); + } + reader.Read(); + reader.Skip(); + } + } + + return typeInfo ?? SessionEventsJsonContext.Default.SessionEventEnvelope; + } +} + +internal sealed class SessionEventJsonTypeInfoResolver : IJsonTypeInfoResolver +{ + internal static SessionEventJsonTypeInfoResolver Default { get; } = new(); + + public JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options) + { + if (type != typeof(SessionEvent)) + { + return null; + } + + JsonTypeInfo typeInfo = JsonMetadataServices.CreateValueInfo( + options, + SessionEventJsonConverter.Default); + typeInfo.PolymorphismOptions = null; + return typeInfo; + } +} + +internal sealed class SessionEventEnvelope +{ + /// Sub-agent instance identifier. Absent for events from the root/main agent and session-level events. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// When true, the event is transient and not persisted to the session event log on disk. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("ephemeral")] + public bool? Ephemeral { get; set; } + + /// Unique event identifier (UUID v4), generated when the event is emitted. + [JsonPropertyName("id")] + public Guid Id { get; set; } + + /// ID of the chronologically preceding event in the session, forming a linked chain. Null for the first event. + [JsonPropertyName("parentId")] + public Guid? ParentId { get; set; } + + /// ISO 8601 timestamp when the event was created. + [JsonPropertyName("timestamp")] + public DateTimeOffset Timestamp { get; set; } + + internal SessionEvent ToSessionEvent() => new() + { + AgentId = AgentId, + Ephemeral = Ephemeral, + Id = Id, + ParentId = ParentId, + Timestamp = Timestamp, + }; +} + /// Session initialization metadata including context and configuration. /// Represents the session.start event. public sealed partial class SessionStartEvent : SessionEvent @@ -595,6 +1117,20 @@ public sealed partial class SessionUsageCheckpointEvent : SessionEvent public required SessionUsageCheckpointData Data { get; set; } } +/// A provider-owned quota observation, distinct from per-call usage and charge accounting. +/// Represents the session.quota_observation event. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed partial class SessionQuotaObservationEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "session.quota_observation"; + + /// The session.quota_observation event payload. + [JsonPropertyName("data")] + public required SessionQuotaObservationData Data { get; set; } +} + /// Working directory and git context at session start. /// Represents the session.context_changed event. public sealed partial class SessionContextChangedEvent : SessionEvent @@ -1137,8 +1673,14 @@ public sealed partial class ToolExecutionStartEvent : SessionEvent public required ToolExecutionStartData Data { get; set; } } -/// Streaming tool execution output for incremental result display. +/// Deprecated merged replacement snapshot of shell output. Use tool.shell_output for append-only, stream-tagged output instead. /// Represents the tool.execution_partial_result event. +[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 public sealed partial class ToolExecutionPartialResultEvent : SessionEvent { /// @@ -1150,6 +1692,19 @@ public sealed partial class ToolExecutionPartialResultEvent : SessionEvent public required ToolExecutionPartialResultData Data { get; set; } } +/// Live, append-only shell output. Not persisted or replayed to late subscribers. Text is decoded and redacted per chunk; chunks need not contain complete lines. +/// Represents the tool.shell_output event. +public sealed partial class ToolShellOutputEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "tool.shell_output"; + + /// The tool.shell_output event payload. + [JsonPropertyName("data")] + public required ToolShellOutputData Data { get; set; } +} + /// Tool execution progress notification with status message. /// Represents the tool.execution_progress event. public sealed partial class ToolExecutionProgressEvent : SessionEvent @@ -1812,6 +2367,20 @@ public sealed partial class SessionAutoModeResolvedEvent : SessionEvent public required SessionAutoModeResolvedData Data { get; set; } } +/// Experimental transient presentation-neutral progress for organization-required plugin preparation. Clients should localize the phase copy and display plugin specs without translating them. +/// Represents the session.managed_plugin_progress event. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed partial class SessionManagedPluginProgressEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "session.managed_plugin_progress"; + + /// The session.managed_plugin_progress event payload. + [JsonPropertyName("data")] + public required SessionManagedPluginProgressData Data { 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. /// Represents the session.managed_settings_resolved event. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] @@ -1826,7 +2395,7 @@ public sealed partial class SessionManagedSettingsResolvedEvent : SessionEvent public required SessionManagedSettingsResolvedData Data { get; set; } } -/// Runtime enforcement of enterprise managed settings: fires when the session blocks or caps a runtime action because enterprise policy governs it, so SDK clients can explain *why* an action was governed. Unlike `session.managed_settings_resolved` (which reports *what* is managed), this reports a concrete governed action — e.g. a user or host tried to turn on a bypass-permissions escalation while policy disables it. Emitted live (not persisted to the session event log) on user/host-initiated attempts only, never for silent policy application. Marked experimental while the managed-settings surface stabilizes. +/// Runtime enforcement of enterprise managed settings: fires when the session blocks or caps a runtime action because enterprise policy governs it, so SDK clients can explain *why* an action was governed. Unlike `session.managed_settings_resolved` (which reports *what* is managed), this reports a concrete governed action — e.g. a user or host tried to turn on bypass permissions or Assisted Permissions while the corresponding policy disables it. Emitted live (not persisted to the session event log) on user/host-initiated attempts only, never for silent policy application. Marked experimental while the managed-settings surface stabilizes. /// Represents the session.managed_settings_enforced event. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed partial class SessionManagedSettingsEnforcedEvent : SessionEvent @@ -1892,6 +2461,19 @@ public sealed partial class ExitPlanModeCompletedEvent : SessionEvent public required ExitPlanModeCompletedData Data { get; set; } } +/// Durable request-correlated evidence for a typed response to a runtime-owned question or plan review. +/// Represents the human_response.recorded event. +public sealed partial class HumanResponseRecordedEvent : SessionEvent +{ + /// + [JsonIgnore] + public override string Type => "human_response.recorded"; + + /// The human_response.recorded event payload. + [JsonPropertyName("data")] + public required HumanResponseRecordedData Data { get; set; } +} + /// Payload of `session.tools_updated` identifying the model whose resolved tools were updated. /// Represents the session.tools_updated event. public sealed partial class SessionToolsUpdatedEvent : SessionEvent @@ -2213,6 +2795,11 @@ public sealed partial class SessionStartData [JsonPropertyName("autoTier")] public AutoTier? AutoTier { get; set; } + /// True when autoTier is a managed-policy default. Omitted for user-authored and legacy values. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("autoTierManaged")] + public bool? AutoTierManaged { get; set; } + /// Working directory and git context at session start. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("context")] @@ -2223,6 +2810,11 @@ public sealed partial class SessionStartData [JsonPropertyName("contextTier")] public ContextTier? ContextTier { get; set; } + /// True when contextTier is a managed-policy default. Omitted for user-authored and legacy values. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("contextTierManaged")] + public bool? ContextTierManaged { get; set; } + /// Version string of the Copilot application. [JsonPropertyName("copilotVersion")] public required string CopilotVersion { get; set; } @@ -2241,11 +2833,26 @@ public sealed partial class SessionStartData [JsonPropertyName("producer")] public required string Producer { get; set; } + /// Provider of selectedModel at creation time, when explicitly selected. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } + /// Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max"). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("reasoningEffort")] public string? ReasoningEffort { get; set; } + /// True when the reasoning effort is a managed-policy default bound to reasoningEffortModel. Omitted for agent-authored, user-authored, independent, and legacy effort. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningEffortManaged")] + public bool? ReasoningEffortManaged { get; set; } + + /// Model that owns effort embedded in an authored model selection. Omitted for independent reasoning-effort overrides and legacy events. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningEffortModel")] + public string? ReasoningEffortModel { get; set; } + /// Reasoning summary mode used for model calls, if applicable (e.g. "none", "concise", "detailed"). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("reasoningSummary")] @@ -2297,6 +2904,11 @@ public sealed partial class SessionResumeData [JsonPropertyName("autoTier")] public AutoTier? AutoTier { get; set; } + /// True when autoTier is a managed-policy default. Omitted for user-authored and legacy values. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("autoTierManaged")] + public bool? AutoTierManaged { get; set; } + /// Updated working directory and git context at resume time. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("context")] @@ -2307,6 +2919,11 @@ public sealed partial class SessionResumeData [JsonPropertyName("contextTier")] public ContextTier? ContextTier { get; set; } + /// True when contextTier is a managed-policy default. Omitted for user-authored and legacy values. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("contextTierManaged")] + public bool? ContextTierManaged { get; set; } + /// When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false or omitted, pending work is normally marked as interrupted unless the resume passively joined live work owned by another client; sessionWasActive distinguishes that case. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("continuePendingWork")] @@ -2321,11 +2938,26 @@ public sealed partial class SessionResumeData [JsonPropertyName("eventsFileSizeBytes")] public long? EventsFileSizeBytes { get; set; } + /// Provider of selectedModel at resume time, when explicitly selected. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } + /// Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max"). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("reasoningEffort")] public string? ReasoningEffort { get; set; } + /// True when the reasoning effort is a managed-policy default bound to reasoningEffortModel. Omitted for agent-authored, user-authored, independent, and legacy effort. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningEffortManaged")] + public bool? ReasoningEffortManaged { get; set; } + + /// Model that owns effort embedded in an authored model selection. Omitted for independent reasoning-effort overrides and legacy events. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningEffortModel")] + public string? ReasoningEffortModel { get; set; } + /// Reasoning summary mode used for model calls, if applicable (e.g. "none", "concise", "detailed"). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("reasoningSummary")] @@ -2603,6 +3235,11 @@ public sealed partial class SessionModelChangeData [JsonPropertyName("autoTier")] public AutoTier? AutoTier { get; set; } + /// True when autoTier is a managed-policy default. Omitted for user-authored and legacy values. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("autoTierManaged")] + public bool? AutoTierManaged { get; set; } + /// Reason the change happened, when not user-initiated. `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path, or `"refusal_fallback"` when the active model declined a request (content refusal) and the runtime switched to the configured refusal-fallback model. UI clients can use this to render contextual copy. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("cause")] @@ -2613,6 +3250,11 @@ public sealed partial class SessionModelChangeData [JsonPropertyName("contextTier")] public ContextTier? ContextTier { get; set; } + /// True when contextTier is a managed-policy default. Omitted for user-authored and legacy values. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("contextTierManaged")] + public bool? ContextTierManaged { get; set; } + /// Newly selected model identifier. [JsonPropertyName("newModel")] public required string NewModel { get; set; } @@ -2627,6 +3269,11 @@ public sealed partial class SessionModelChangeData [JsonPropertyName("previousModel")] public string? PreviousModel { get; set; } + /// Provider of previousModel, when known. A provider-only change is a model selection change even when the model identifiers are equal. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("previousProviderId")] + public string? PreviousProviderId { get; set; } + /// Reasoning effort level before the model change, if applicable. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("previousReasoningEffort")] @@ -2642,11 +3289,26 @@ public sealed partial class SessionModelChangeData [JsonPropertyName("previousVerbosity")] public Verbosity? PreviousVerbosity { get; set; } + /// Provider selected for newModel. Omitted for legacy or unattributed selections; never inferred from a later selection. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } + /// Reasoning effort level after the model change, if applicable. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("reasoningEffort")] public string? ReasoningEffort { get; set; } + /// True when the reasoning effort is a managed-policy default bound to reasoningEffortModel. Omitted for agent-authored, user-authored, independent, and legacy effort. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningEffortManaged")] + public bool? ReasoningEffortManaged { get; set; } + + /// Model that owns effort embedded in an authored model selection. Omitted for independent reasoning-effort overrides and legacy events. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningEffortModel")] + public string? ReasoningEffortModel { get; set; } + /// Reasoning summary mode after the model change, if applicable. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("reasoningSummary")] @@ -2875,6 +3537,12 @@ public sealed partial class SessionSnapshotRewindData /// Session termination metrics including usage statistics, code changes, and shutdown reason. public sealed partial class SessionShutdownData { + /// Complete accounting state captured atomically with receipt watermarks. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude] + [JsonPropertyName("accountingSnapshot")] + internal UsageGetMetricsResult? AccountingSnapshot { get; set; } + /// Per-agent usage breakdown, keyed by agent instance identifier. The main conversation uses the stable key `main`. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("agentMetrics")] @@ -2913,6 +3581,11 @@ public sealed partial class SessionShutdownData [JsonPropertyName("modelMetrics")] public required IDictionary ModelMetrics { get; set; } + /// Provider/model accounting preserved for replay without consulting current provider selection. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerModelMetrics")] + public UsageMetricsProviderModelMetric[]? ProviderModelMetrics { get; set; } + /// Unix timestamp (milliseconds) when the session started. [JsonPropertyName("sessionStartTime")] public required long SessionStartTime { get; set; } @@ -2952,11 +3625,22 @@ public sealed partial class SessionShutdownData [JsonInclude] [JsonPropertyName("totalPremiumRequests")] internal double? TotalPremiumRequests { get; set; } + + /// Per-source accounting sequence watermarks subsuming preceding usage receipts. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("usageAccountingWatermarks")] + public IDictionary? UsageAccountingWatermarks { get; set; } } /// Durable session usage checkpoint for reconstructing aggregate accounting on resume. public sealed partial class SessionUsageCheckpointData { + /// Complete accounting state captured atomically with the receipt watermarks. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude] + [JsonPropertyName("accountingSnapshot")] + internal UsageGetMetricsResult? AccountingSnapshot { get; set; } + /// Internal per-model prompt-cache state used to restore expiration tracking on resume. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonInclude] @@ -2969,6 +3653,11 @@ public sealed partial class SessionUsageCheckpointData [JsonPropertyName("promptCacheBreakState")] internal JsonElement[]? PromptCacheBreakState { get; set; } + /// Provider/model accounting snapshot that subsumes preceding usage receipts. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerModelMetrics")] + public UsageMetricsProviderModelMetric[]? ProviderModelMetrics { get; set; } + /// Session-wide accumulated nano-AI units cost at checkpoint time. [JsonPropertyName("totalNanoAiu")] public required double TotalNanoAiu { get; set; } @@ -2978,6 +3667,20 @@ public sealed partial class SessionUsageCheckpointData [JsonInclude] [JsonPropertyName("totalPremiumRequests")] internal double? TotalPremiumRequests { get; set; } + + /// Highest accounted sequence per source session; no per-call deduplication history is retained. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("usageAccountingWatermarks")] + public IDictionary? UsageAccountingWatermarks { get; set; } +} + +/// A provider-owned quota observation, distinct from per-call usage and charge accounting. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed partial class SessionQuotaObservationData +{ + /// The admitted provider's state observation. Admission observations never contain quantities or reset/percentage semantics. + [JsonPropertyName("observation")] + public required ProviderQuotaState Observation { get; set; } } /// Working directory and git context at session start. @@ -4160,6 +4863,8 @@ public sealed partial class AssistantMessageData [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 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -4170,6 +4875,11 @@ public sealed partial class AssistantMessageData [JsonPropertyName("phase")] public string? Phase { get; set; } + /// Opaque origin provider identity captured at dispatch. Only an exact match with the target provider permits replay of reasoningOpaque, encryptedContent, reasoningBlocks, serverTools, and provider response/item identifiers. Matching model ids or wire protocols is insufficient. Omitted for legacy or unattributed messages; the runtime excludes unproven opaque state when projecting to a known provider. Text content and client tool requests/results remain portable semantic history. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerId")] + public string? ProviderId { get; set; } + /// Neutral provider-tagged reasoning content blocks preserved verbatim for round-tripping. `reasoningText` and `reasoningOpaque` are a lossy derived view of these blocks, retained for display. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("reasoningBlocks")] @@ -4249,6 +4959,8 @@ public sealed partial class AssistantMessageDeltaData [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 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -4290,6 +5002,16 @@ public sealed partial class AssistantUsageData [JsonPropertyName("acceptedPredictionTokens")] public long? AcceptedPredictionTokens { get; set; } + /// Runtime-owned identity shared with the durable accounting receipt. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("accounting")] + public UsageAccountingIdentity? Accounting { get; set; } + + /// Availability of this call's reported AI-credit amount. Missing billing is unavailable, not a zero charge. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("aiCreditsStatus")] + public AiCreditsStatus? AiCreditsStatus { get; set; } + /// Completion ID from the model provider (e.g., chatcmpl-abc123). [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("apiCallId")] @@ -4306,6 +5028,11 @@ public sealed partial class AssistantUsageData [JsonPropertyName("availableToolCount")] internal long? AvailableToolCount { get; set; } + /// Where the bring-your-own-key model runs and who manages it: "local_managed" (on the device, managed by Copilot), "local_user" (on the device, managed by the user), or "remote_user" (off the device, managed by the user). Absent for Copilot-served models. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("byokKind")] + public string? ByokKind { get; set; } + /// Whether the provider reported prompt-cache usage details for this call. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonInclude] @@ -4417,6 +5144,16 @@ public sealed partial class AssistantUsageData [JsonPropertyName("model")] public required string Model { get; set; } + /// Model display name captured for this call, not derived from the current selection. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("modelDisplayName")] + public string? ModelDisplayName { get; set; } + + /// Fixed-set provider family serving the bring-your-own-key model (for example "openai", "anthropic", "azure_openai", "ollama", "llama_cpp", or "other"). Never the caller-supplied provider name. Absent for Copilot-served models. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("modelProvider")] + public string? ModelProvider { get; set; } + /// Number of tool calls returned by the model. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonInclude] @@ -4438,11 +5175,18 @@ public sealed partial class AssistantUsageData [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 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] public string? ParentToolCallId { get; set; } + /// Provider identity and product label captured by the actual call's dispatch path. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("provider")] + public ModelProviderRef? Provider { get; set; } + /// GitHub request tracing ID (x-github-request-id header) for server-side log correlation. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("providerCallId")] @@ -4474,6 +5218,11 @@ public sealed partial class AssistantUsageData [JsonPropertyName("rejectedPredictionTokens")] public long? RejectedPredictionTokens { get; set; } + /// Serialized (uncompressed) byte length of the request body. A content-free size signal. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requestBodyBytes")] + public long? RequestBodyBytes { get; set; } + /// Per-request treatment/eligibility signal returned by the Copilot API in the `X-GitHub-Copilot-Request-TE` response header for the associated model call; `false` when the header was absent or unparseable. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("rte")] @@ -4518,6 +5267,17 @@ public sealed partial class AssistantUsageData [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("transport")] public AssistantUsageTransport? Transport { get; set; } + + /// Milliseconds spent on the WebSocket attempt before falling back to HTTP. + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackAfterMs")] + public TimeSpan? WebsocketFallbackAfter { get; set; } + + /// Why the call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackReason")] + public ModelCallWebSocketFallbackReason? WebsocketFallbackReason { get; set; } } /// A detected loss of a previously cached prompt prefix. @@ -4625,6 +5385,12 @@ public sealed partial class PromptCacheBreakData [JsonPropertyName("toolsRedefined")] internal string[]? ToolsRedefined { get; set; } + /// Changed definition parts of redefined tools, as `tool:part` entries; property-level parts only for telemetry-safe tools, whose other names are hashed. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonInclude] + [JsonPropertyName("toolsRedefinedParts")] + internal string[]? ToolsRedefinedParts { get; set; } + /// Raw names of tools redefined since the prior call, restricted because a tool name can be user-authored. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonInclude] @@ -4668,6 +5434,11 @@ public sealed partial class ModelCallFailureData [JsonPropertyName("badRequestKind")] public ModelCallFailureBadRequestKind? BadRequestKind { get; set; } + /// Where the bring-your-own-key model for the failed call runs and who manages it: "local_managed" (on the device, managed by Copilot), "local_user" (on the device, managed by the user), or "remote_user" (off the device, managed by the user). Absent for Copilot-served models. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("byokKind")] + public string? ByokKind { get; set; } + /// Duration of the failed API call in milliseconds. [JsonConverter(typeof(MillisecondsTimeSpanConverter))] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -4735,6 +5506,11 @@ public sealed partial class ModelCallFailureData [JsonPropertyName("model")] public string? Model { get; set; } + /// Fixed-set provider family serving the bring-your-own-key model for the failed call (for example "openai", "anthropic", "azure_openai", "ollama", "llama_cpp", or "other"). Never the caller-supplied provider name. Absent for Copilot-served models. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("modelProvider")] + public string? ModelProvider { get; set; } + /// Parent task tool call ID when this failed model call belongs to a sub-agent. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -4756,11 +5532,21 @@ public sealed partial class ModelCallFailureData [JsonPropertyName("reasoningEffort")] public string? ReasoningEffort { get; set; } + /// Serialized (uncompressed) byte length of the failed request body. A content-free size signal. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("requestBodyBytes")] + public long? RequestBodyBytes { get; set; } + /// Content-free structural summary of the failing request. Contains only counts and shape flags (no prompt content), so it is safe for unrestricted telemetry. Populated only for client-error (4xx) failures. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("requestFingerprint")] public ModelCallFailureRequestFingerprint? RequestFingerprint { get; set; } + /// Zero-based orchestrator retry index of the failed attempt. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("retryAttempt")] + public long? RetryAttempt { get; set; } + /// Per-request treatment/eligibility signal returned by the Copilot API in the `X-GitHub-Copilot-Request-TE` response header for the associated model call; `false` when the header was absent or unparseable. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("rte")] @@ -4784,6 +5570,17 @@ public sealed partial class ModelCallFailureData [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("transport")] public ModelCallFailureTransport? Transport { get; set; } + + /// Milliseconds spent on the WebSocket attempt before falling back to HTTP. + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackAfterMs")] + public TimeSpan? WebsocketFallbackAfter { get; set; } + + /// Why the failed call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("websocketFallbackReason")] + public ModelCallWebSocketFallbackReason? WebsocketFallbackReason { get; set; } } /// Internal telemetry result for one logical model operation after all orchestrator-owned retries settle. @@ -4942,6 +5739,8 @@ public sealed partial class ToolExecutionStartData [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 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -4981,10 +5780,16 @@ public sealed partial class ToolExecutionStartData public string? TurnId { get; set; } } -/// Streaming tool execution output for incremental result display. +/// Deprecated merged replacement snapshot of shell output. Use tool.shell_output for append-only, stream-tagged output instead. +[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 public sealed partial class ToolExecutionPartialResultData { - /// Incremental output chunk from the running tool. + /// Merged replacement snapshot from the running shell, not an append-only chunk. [JsonPropertyName("partialOutput")] public required string PartialOutput { get; set; } @@ -4993,6 +5798,27 @@ public sealed partial class ToolExecutionPartialResultData public required string ToolCallId { get; set; } } +/// Live, append-only shell output. Not persisted or replayed to late subscribers. Text is decoded and redacted per chunk; chunks need not contain complete lines. +public sealed partial class ToolShellOutputData +{ + /// Zero-based publication sequence across all output streams for this tool call. Not a byte offset or an OS write-order guarantee. + [JsonPropertyName("sequence")] + public required long Sequence { get; set; } + + /// Output source. Omission means stdout. Terminal output has no separate stdout/stderr attribution. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("stream")] + public ToolShellOutputStream? Stream { get; set; } + + /// New output to append, without synthetic shell-result markers or stream-switch separators. + [JsonPropertyName("text")] + public required string Text { get; set; } + + /// Tool call ID that owns this shell output. + [JsonPropertyName("toolCallId")] + public required string ToolCallId { get; set; } +} + /// Tool execution progress notification with status message. public sealed partial class ToolExecutionProgressData { @@ -5000,6 +5826,12 @@ public sealed partial class ToolExecutionProgressData [JsonPropertyName("progressMessage")] public required string ProgressMessage { get; set; } + /// Client-only structured progress metadata. Not model-facing tool output. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("structuredContent")] + public JsonElement? StructuredContent { get; set; } + /// Tool call ID this progress notification belongs to. [JsonPropertyName("toolCallId")] public required string ToolCallId { get; set; } @@ -5013,6 +5845,12 @@ public sealed partial class ToolExecutionCompleteData [JsonPropertyName("error")] public ToolExecutionCompleteError? Error { get; set; } + /// Experimental. File mutations actually committed by a built-in file editing tool, in execution order. Present on successful edits and on partial failures when earlier mutations were committed. Paths are absolute in the session filesystem namespace. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("fileEdits")] + public ToolExecutionCompleteFileEdit[]? FileEdits { get; set; } + /// Experimental HydraFusion attribution for this tool completion. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -5044,6 +5882,8 @@ public sealed partial class ToolExecutionCompleteData [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 [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("parentToolCallId")] @@ -6427,12 +7267,30 @@ public sealed partial class SessionAutoModeResolvedData [JsonPropertyName("routingMethod")] public string? RoutingMethod { get; set; } + /// Short human-readable sentence from the routing service explaining why this model was chosen, for display alongside the model. Present only when the service supplied one: it is omitted for on-device selections, when the service did not provide an explanation, and when a replayed decision made no routing call. The text is display-only and drawn from a fixed catalogue; several distinct routing categories share identical wording, so it cannot be used to recover the category or keyed on programmatically. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("selectionReason")] + public string? SelectionReason { get; set; } + /// Whether a sticky model choice overrode the router result. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("stickyOverride")] public bool? StickyOverride { get; set; } } +/// Experimental transient presentation-neutral progress for organization-required plugin preparation. Clients should localize the phase copy and display plugin specs without translating them. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed partial class SessionManagedPluginProgressData +{ + /// Current preparation phase. + [JsonPropertyName("phase")] + public required ManagedPluginProgressPhase Phase { get; set; } + + /// Ordered plugin install specs affected by this phase. Empty while managed settings initialize and when preparation completes. + [JsonPropertyName("pluginSpecs")] + public required string[] PluginSpecs { 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. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed partial class SessionManagedSettingsResolvedData @@ -6487,7 +7345,7 @@ public sealed partial class SessionManagedSettingsResolvedData public required ManagedSettingsResolvedSource Source { get; set; } } -/// Runtime enforcement of enterprise managed settings: fires when the session blocks or caps a runtime action because enterprise policy governs it, so SDK clients can explain *why* an action was governed. Unlike `session.managed_settings_resolved` (which reports *what* is managed), this reports a concrete governed action — e.g. a user or host tried to turn on a bypass-permissions escalation while policy disables it. Emitted live (not persisted to the session event log) on user/host-initiated attempts only, never for silent policy application. Marked experimental while the managed-settings surface stabilizes. +/// Runtime enforcement of enterprise managed settings: fires when the session blocks or caps a runtime action because enterprise policy governs it, so SDK clients can explain *why* an action was governed. Unlike `session.managed_settings_resolved` (which reports *what* is managed), this reports a concrete governed action — e.g. a user or host tried to turn on bypass permissions or Assisted Permissions while the corresponding policy disables it. Emitted live (not persisted to the session event log) on user/host-initiated attempts only, never for silent policy application. Marked experimental while the managed-settings surface stabilizes. [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] public sealed partial class SessionManagedSettingsEnforcedData { @@ -6495,7 +7353,7 @@ public sealed partial class SessionManagedSettingsEnforcedData [JsonPropertyName("action")] public required ManagedSettingsEnforcedAction Action { get; set; } - /// For a `bypass_permissions_blocked` action, which permission-escalation primitive was refused. Absent for actions without a specific escalation primitive. + /// For a `bypass_permissions_blocked` action, which permission-mode or escalation primitive was refused. Absent for actions without a specific primitive. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("escalation")] public ManagedSettingsEnforcedEscalation? Escalation { get; set; } @@ -6508,7 +7366,7 @@ public sealed partial class SessionManagedSettingsEnforcedData [JsonPropertyName("message")] public required string Message { get; set; } - /// The managed setting key responsible for the enforcement (e.g. `permissions.disableBypassPermissionsMode`). + /// The managed setting key responsible for the enforcement (for example `permissions.disableBypassPermissionsMode` or `permissions.disableAssistedPermissionsMode`). [JsonPropertyName("setting")] public required string Setting { get; set; } } @@ -6587,6 +7445,27 @@ public sealed partial class ExitPlanModeCompletedData public ExitPlanModeAction? SelectedAction { get; set; } } +/// Durable request-correlated evidence for a typed response to a runtime-owned question or plan review. +public sealed partial class HumanResponseRecordedData +{ + /// Controlled actor provenance established at response ingress. + [JsonPropertyName("actor")] + public required HumanResponseActor Actor { get; set; } + + /// Request ID of the runtime-owned question or plan review. + [JsonPropertyName("requestId")] + public required string RequestId { get; set; } + + /// Typed request and response payload. + [JsonPropertyName("response")] + public required HumanResponseRecordedResponse Response { get; set; } + + /// Tool call ID that opened the request, when present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("toolCallId")] + public string? ToolCallId { get; set; } +} + /// Payload of `session.tools_updated` identifying the model whose resolved tools were updated. public sealed partial class SessionToolsUpdatedData { @@ -7159,41 +8038,291 @@ public sealed partial class HandoffRepository public required string Owner { get; set; } } -/// Request count and cost metrics. -/// Nested data type for ShutdownModelMetricRequests. -public sealed partial class ShutdownModelMetricRequests +/// Request count and cost metrics for this model. +/// Nested data type for UsageMetricsModelMetricRequests. +public sealed partial class UsageMetricsModelMetricRequests { - /// Cumulative cost multiplier for requests to this model. - [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// User-initiated premium request cost (with multiplier applied). [JsonPropertyName("cost")] - public double? Cost { get; set; } + public required double Cost { get; set; } - /// Total number of API requests made to this model. - [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + /// Number of API requests made with this model. [JsonPropertyName("count")] - public long? Count { get; set; } + public required long Count { get; set; } } -/// A token-type entry in a shutdown model metric, storing the accumulated token count. -/// Nested data type for ShutdownModelMetricTokenDetail. -public sealed partial class ShutdownModelMetricTokenDetail +/// Per-model token-detail entry containing the accumulated token count for one token type. +/// Nested data type for UsageMetricsModelMetricTokenDetail. +public sealed partial class UsageMetricsModelMetricTokenDetail { /// Accumulated token count for this token type. [JsonPropertyName("tokenCount")] public required long TokenCount { get; set; } } -/// Token usage breakdown. -/// Nested data type for ShutdownModelMetricUsage. -public sealed partial class ShutdownModelMetricUsage +/// Token usage metrics for this model. +/// Nested data type for UsageMetricsModelMetricUsage. +public sealed partial class UsageMetricsModelMetricUsage { - /// Total tokens read from prompt cache across all requests. + /// Total tokens read from prompt cache. [JsonPropertyName("cacheReadTokens")] public required long CacheReadTokens { get; set; } - /// Total tokens written to prompt cache across all requests. + /// Total tokens written to prompt cache. + [JsonPropertyName("cacheWriteTokens")] + public required long CacheWriteTokens { get; set; } + + /// Total input tokens consumed. + [JsonPropertyName("inputTokens")] + public required long InputTokens { get; set; } + + /// Total output tokens produced. + [JsonPropertyName("outputTokens")] + public required long OutputTokens { get; set; } + + /// Total output tokens used for reasoning. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningTokens")] + public long? ReasoningTokens { get; set; } +} + +/// Per-model usage metrics, including request counts/costs, token usage, nano-AI units, and per-token-type details. +/// Nested data type for UsageMetricsModelMetric. +public sealed partial class UsageMetricsModelMetric +{ + /// Completeness of this model's reported AI-credit subtotal. Explicitly reported zero is complete, not unavailable. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("aiCreditsStatus")] + public AiCreditsStatus? AiCreditsStatus { get; set; } + + /// Latest known prompt-cache expiration for this model. A timestamp in the past indicates that the observed cache has expired. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cacheExpiresAt")] + public DateTimeOffset? CacheExpiresAt { get; set; } + + /// Request count and cost metrics for this model. + [JsonPropertyName("requests")] + public required UsageMetricsModelMetricRequests Requests { get; set; } + + /// Token count details per type. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenDetails")] + public IDictionary? TokenDetails { get; set; } + + /// Accumulated nano-AI units cost for this model. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("totalNanoAiu")] + public double? TotalNanoAiu { get; set; } + + /// Token usage metrics for this model. + [JsonPropertyName("usage")] + public required UsageMetricsModelMetricUsage Usage { get; set; } +} + +/// Usage attributed to one agent instance, including its identity, API duration, AI units, and per-model breakdown. +/// Nested data type for UsageMetricsAgentMetric. +public sealed partial class UsageMetricsAgentMetric +{ + /// Human-readable label for this subagent invocation, copied from the originating `subagent.started` event. For task-tool subagents this is the invocation's task description rather than the agent's configured display name, so group by `agentName` for stable per-agent labels. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("agentDisplayName")] + public string? AgentDisplayName { get; set; } + + /// Configured agent name, when this is a subagent. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("agentName")] + public string? AgentName { get; set; } + + /// Per-model usage for this agent, keyed by model identifier. + [JsonPropertyName("modelMetrics")] + public required IDictionary ModelMetrics { get; set; } + + /// Time spent in model API calls by this agent, in milliseconds. + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonPropertyName("totalApiDurationMs")] + public required TimeSpan TotalApiDuration { get; set; } + + /// Accumulated nano-AI units cost for this agent. + [JsonPropertyName("totalNanoAiu")] + public required double TotalNanoAiu { get; set; } +} + +/// Aggregated code change metrics. +/// Nested data type for UsageMetricsCodeChanges. +public sealed partial class UsageMetricsCodeChanges +{ + /// Distinct file paths modified during the session. + [JsonPropertyName("filesModified")] + public required string[] FilesModified { get; set; } + + /// Number of distinct files modified. + [JsonPropertyName("filesModifiedCount")] + public required long FilesModifiedCount { get; set; } + + /// Total lines of code added. + [JsonPropertyName("linesAdded")] + public required long LinesAdded { get; set; } + + /// Total lines of code removed. + [JsonPropertyName("linesRemoved")] + public required long LinesRemoved { get; set; } +} + +/// A neutral reference to the model provider that produced a model: an opaque id, a human-readable label, and the provider kind. Carried on each enumerated Model so consumers can group by provider without reaching into a provider-shaped internal type. +/// Nested data type for ModelProviderRef. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed partial class ModelProviderRef +{ + /// Opaque, stable id of the provider that produced this model. Matches the enumerated `ModelProviderDescriptor.id`. + [JsonPropertyName("id")] + public required string Id { get; set; } + + /// The provider kind. + [JsonPropertyName("kind")] + public required ModelProviderKind Kind { get; set; } + + /// Human-readable provider label, owned by the runtime so every consumer renders identical text. + [JsonPropertyName("label")] + public required string Label { get; set; } +} + +/// Usage for one recorded provider and model, without merging identical model IDs across providers. +/// Nested data type for UsageMetricsProviderModelMetric. +public sealed partial class UsageMetricsProviderModelMetric +{ + /// Request, token, and cost totals for this provider/model. + [JsonPropertyName("metrics")] + public required UsageMetricsModelMetric Metrics { get; set; } + + /// Model display name captured at call time, when known. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("modelDisplayName")] + public string? ModelDisplayName { get; set; } + + /// Model identity, or null for legacy aggregate-only usage. + [JsonPropertyName("modelId")] + public string? ModelId { get; set; } + + /// Provider identity and product label captured when the call was dispatched; null when unknown. + [JsonPropertyName("provider")] + public ModelProviderRef? Provider { get; set; } +} + +/// Session-wide token-detail entry containing the accumulated token count for one token type. +/// Nested data type for UsageMetricsTokenDetail. +public sealed partial class UsageMetricsTokenDetail +{ + /// Accumulated token count for this token type. + [JsonPropertyName("tokenCount")] + public required long TokenCount { get; set; } +} + +/// Accumulated session usage metrics, including premium request cost, token counts, model breakdown, and code-change totals. +/// Nested data type for UsageGetMetricsResult. +public sealed partial class UsageGetMetricsResult +{ + /// Per-agent usage metrics, keyed by agent instance identifier. The main conversation uses the stable key `main`. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("agentMetrics")] + public IDictionary? AgentMetrics { get; set; } + + /// Completeness of totalNanoAiu. Numeric totals remain the reported subtotal; zero is not a claim of free usage when unavailable. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("aiCreditsStatus")] + public AiCreditsStatus? AiCreditsStatus { get; set; } + + /// Aggregated code change metrics. + [JsonPropertyName("codeChanges")] + public required UsageMetricsCodeChanges CodeChanges { get; set; } + + /// Currently active model identifier. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("currentModel")] + public string? CurrentModel { get; set; } + + /// Input tokens from the most recent main-agent API call. + [JsonPropertyName("lastCallInputTokens")] + public required long LastCallInputTokens { get; set; } + + /// Output tokens from the most recent main-agent API call. + [JsonPropertyName("lastCallOutputTokens")] + public required long LastCallOutputTokens { get; set; } + + /// Per-model token and request metrics, keyed by model identifier. + [JsonPropertyName("modelMetrics")] + public required IDictionary ModelMetrics { get; set; } + + /// + /// Provider-attributed model metrics, ordered by first observed provider and model. + /// Null attribution preserves historical usage whose provider or model was not recorded. + /// + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("providerModelMetrics")] + public UsageMetricsProviderModelMetric[]? ProviderModelMetrics { get; set; } + + /// ISO 8601 timestamp when the session started. + [JsonPropertyName("sessionStartTime")] + public required DateTimeOffset SessionStartTime { get; set; } + + /// Session-wide per-token-type accumulated token counts. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenDetails")] + public IDictionary? TokenDetails { get; set; } + + /// Total time spent in model API calls (milliseconds). + [JsonConverter(typeof(MillisecondsTimeSpanConverter))] + [JsonPropertyName("totalApiDurationMs")] + public required TimeSpan TotalApiDuration { get; set; } + + /// Session-wide accumulated nano-AI units cost. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("totalNanoAiu")] + public double? TotalNanoAiu { get; set; } + + /// Total user-initiated premium request cost across all models (may be fractional due to multipliers). + [JsonPropertyName("totalPremiumRequestCost")] + public required double TotalPremiumRequestCost { get; set; } + + /// Raw count of user-initiated API requests. + [JsonPropertyName("totalUserRequests")] + public required long TotalUserRequests { get; set; } +} + +/// Request count and cost metrics. +/// Nested data type for ShutdownModelMetricRequests. +public sealed partial class ShutdownModelMetricRequests +{ + /// Cumulative cost multiplier for requests to this model. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("cost")] + public double? Cost { get; set; } + + /// Total number of API requests made to this model. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("count")] + public long? Count { get; set; } +} + +/// A token-type entry in a shutdown model metric, storing the accumulated token count. +/// Nested data type for ShutdownModelMetricTokenDetail. +public sealed partial class ShutdownModelMetricTokenDetail +{ + /// Accumulated token count for this token type. + [JsonPropertyName("tokenCount")] + public required long TokenCount { get; set; } +} + +/// Token usage breakdown. +/// Nested data type for ShutdownModelMetricUsage. +public sealed partial class ShutdownModelMetricUsage +{ + /// Total tokens read from prompt cache across all requests. + [JsonPropertyName("cacheReadTokens")] + public required long CacheReadTokens { get; set; } + + /// Total tokens written to prompt cache across all requests. [JsonPropertyName("cacheWriteTokens")] public required long CacheWriteTokens { get; set; } @@ -7307,6 +8436,155 @@ internal sealed partial class UsageCheckpointModelCacheState public required string ModelId { get; set; } } +/// Authoritative budget measurements and policy metadata, independent of provider. +/// Nested data type for ProviderQuotaBudgetMetadata. +public sealed partial class ProviderQuotaBudgetMetadata +{ + /// Exact measured consumption in the budget's unit. + [JsonPropertyName("consumed")] + public required double Consumed { get; set; } + + /// Exact budget entitlement, retaining incumbent fractional-unit compatibility. + [JsonPropertyName("entitlement")] + public required double Entitlement { get; set; } + + /// Usage beyond entitlement, in the budget's unit. + [JsonPropertyName("overage")] + public required double Overage { get; set; } + + /// Whether additional usage is allowed when the budget is exhausted. + [JsonPropertyName("overageAllowedWhenExhausted")] + public required bool OverageAllowedWhenExhausted { get; set; } + + /// Optional additional-usage budget cap. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("overageLimit")] + public double? OverageLimit { get; set; } + + /// Service-reported remaining percentage for authoritative budget presentation. + [JsonPropertyName("remainingPercentage")] + public required double RemainingPercentage { get; set; } + + /// Reset instant in epoch milliseconds when this budget actually defines a window. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resetAtEpochMs")] + public double? ResetAtEpochMs { get; set; } + + /// Whether the reset instant is an estimate. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("resetEstimated")] + public bool? ResetEstimated { get; set; } + + /// Whether this budget uses token-based billing. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("tokenBasedBilling")] + public bool? TokenBasedBilling { get; set; } + + /// Whether the budget has unlimited entitlement. + [JsonPropertyName("unlimited")] + public required bool Unlimited { get; set; } + + /// Whether service policy allows continued usage after exhaustion. + [JsonPropertyName("usageAllowedWhenExhausted")] + public required bool UsageAllowedWhenExhausted { get; set; } +} + +/// An account quota reading. Absence of a quantity is unknown, never zero. +/// Nested data type for ProviderQuotaState. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed partial class ProviderQuotaState +{ + /// Whether the service permits access, independently of balance. + [JsonPropertyName("accessState")] + public required ProviderQuotaAccessState AccessState { get; set; } + + /// Service error code or client acquisition category, separate from the business-state reason. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("acquisitionError")] + public string? AcquisitionError { get; set; } + + /// Acquisition outcome. Unavailable/failed readings have no service verdict; access/capacity unavailable are compatibility placeholders only. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("acquisitionStatus")] + public ProviderQuotaAcquisitionStatus? AcquisitionStatus { get; set; } + + /// Independently reported signed 64-bit available quantity. Zero does not override the service access/capacity verdict; omission and null are preserved. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("availableQuantity")] + public long? AvailableQuantity { get; set; } + + /// Optional metadata for an authoritative budget. Advisory balances and admission-only observations do not populate this. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("budgetMetadata")] + public ProviderQuotaBudgetMetadata? BudgetMetadata { get; set; } + + /// Service capacity state; missing quantities do not imply exhaustion. + [JsonPropertyName("capacityState")] + public required ProviderQuotaCapacityState CapacityState { get; set; } + + /// Key for the backwards-compatible snapshots projection, when the authoritative budget supports that contract. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("compatibilityKey")] + public string? CompatibilityKey { get; set; } + + /// Independently reported signed 64-bit entitlement. -1 is an unlimited sentinel, not a capacity-state rewrite; omission and null are preserved. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("entitledQuantity")] + public long? EntitledQuantity { get; set; } + + /// Explicit service admission flag, when reported. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("hasQuota")] + public bool? HasQuota { get; set; } + + /// HTTP status from acquisition, when available. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("httpStatus")] + public long? HttpStatus { get; set; } + + /// Whether this is a GET account reading or a pre-response admission observation. Observations are never merged across kinds. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("observationKind")] + public ProviderQuotaObservationKind? ObservationKind { get; set; } + + /// When the runtime observed this reading, not a charge timestamp or guarantee that consumption has settled. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("observedAt")] + public string? ObservedAt { get; set; } + + /// Provider/account whose service reported this state. + [JsonPropertyName("provider")] + public required ModelProviderRef Provider { get; set; } + + /// Whether quantities are authoritative budget measurements, advisory balances, or absent. Only authoritative budgets support percentage presentation. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("quantityKind")] + public ProviderQuotaQuantityKind? QuantityKind { get; set; } + + /// Service-owned quota identifier within this provider. + [JsonPropertyName("quotaId")] + public required string QuotaId { get; set; } + + /// Service-reported explanation for the state. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// Service name owning this reading. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("service")] + public string? Service { get; set; } + + /// Service-reported quota source. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("source")] + public string? Source { get; set; } + + /// Unit of entitledQuantity and availableQuantity. + [JsonPropertyName("unit")] + public required ProviderQuotaUnit Unit { get; set; } +} + /// Token usage detail for a single billing category. /// Nested data type for CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail. public sealed partial class CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail @@ -7358,6 +8636,11 @@ internal sealed partial class CompactionCompleteCompactionTokensUsedCopilotUsage /// Nested data type for CompactionCompleteCompactionTokensUsed. public sealed partial class CompactionCompleteCompactionTokensUsed { + /// Whether the compaction's credit amount was reported for all, some, or none of its calls. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("aiCreditsStatus")] + public AiCreditsStatus? AiCreditsStatus { get; set; } + /// Cached input tokens reused in the compaction LLM call. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("cacheReadTokens")] @@ -7390,10 +8673,20 @@ public sealed partial class CompactionCompleteCompactionTokensUsed [JsonPropertyName("model")] public string? Model { get; set; } + /// Model display name captured for the compaction call. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("modelDisplayName")] + public string? ModelDisplayName { get; set; } + /// Output tokens produced by the compaction LLM call. [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonPropertyName("outputTokens")] public long? OutputTokens { get; set; } + + /// Provider captured for the compaction call; absent when attribution is unknown. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("provider")] + public ModelProviderRef? Provider { get; set; } } /// Original request-level and effective conversation reasoning effort for a provider history boundary; the historical type name is retained for compatibility. @@ -8452,6 +9745,11 @@ public sealed partial class AssistantMessageReasoningBlocks [JsonPropertyName("blocks")] public JsonElement[]? Blocks { get; set; } + /// Anthropic Messages assistant block ordering preserved when the legacy reasoning-only representation cannot reproduce it exactly. Thinking and text blocks remain verbatim; tool-use entries retain identity and a payload fingerprint when later signed reasoning depends on them, and are hydrated from the message's tool requests during replay. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("orderedBlocks")] + public JsonElement[]? OrderedBlocks { get; set; } + /// Model provider that produced these reasoning blocks. [JsonPropertyName("provider")] public required string Provider { get; set; } @@ -8548,6 +9846,23 @@ public sealed partial class AssistantMessageToolRequest public AssistantMessageToolRequestType? Type { get; set; } } +/// Ordered accounting identity assigned under the source session's emission lock. +/// Nested data type for UsageAccountingIdentity. +public sealed partial class UsageAccountingIdentity +{ + /// Monotonically increasing sequence within the source session. + [JsonPropertyName("sequence")] + public required long Sequence { get; set; } + + /// Session that assigned this accounting sequence. + [JsonPropertyName("sourceSessionId")] + public required string SourceSessionId { get; set; } + + /// Existing API call identifier, or a runtime-generated identity when none was supplied. + [JsonPropertyName("usageId")] + public required string UsageId { get; set; } +} + /// Token usage detail for a single billing category. /// Nested data type for AssistantUsageCopilotUsageTokenDetail. public sealed partial class AssistantUsageCopilotUsageTokenDetail @@ -8662,6 +9977,16 @@ internal sealed partial class AssistantUsageQuotaSnapshot /// Nested data type for ModelCallFailureRequestFingerprint. public sealed partial class ModelCallFailureRequestFingerprint { + /// Summed byte length of opaque or encrypted reasoning payloads. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("encryptedContentBytes")] + public long? EncryptedContentBytes { get; set; } + + /// Summed byte length of inline image payloads (data URLs and base64 sources). + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("imageBytes")] + public long? ImageBytes { get; set; } + /// Total number of image content parts. [JsonPropertyName("imagePartCount")] public required long ImagePartCount { get; set; } @@ -8683,6 +10008,11 @@ public sealed partial class ModelCallFailureRequestFingerprint [JsonPropertyName("namelessToolCallCount")] public required long NamelessToolCallCount { get; set; } + /// Number of messages carrying opaque or encrypted reasoning. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("reasoningItemCount")] + public long? ReasoningItemCount { get; set; } + /// Total number of tool calls across assistant messages. [JsonPropertyName("toolCallCount")] public required long ToolCallCount { get; set; } @@ -8774,12 +10104,26 @@ public sealed partial class ToolExecutionCompleteError public RemediationAction? Remediation { get; set; } } -/// Binary result returned by a tool for the model. -/// Nested data type for PersistedBinaryImage. -public sealed partial class PersistedBinaryImage +/// A file mutation that was actually committed by a built-in file editing tool. +/// Nested data type for ToolExecutionCompleteFileEdit. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +public sealed partial class ToolExecutionCompleteFileEdit { - /// Base64-encoded binary data. - [Base64String] + /// Kind of mutation committed at this path. + [JsonPropertyName("kind")] + public required ToolExecutionCompleteFileEditKind Kind { get; set; } + + /// Absolute path in the session filesystem namespace. + [JsonPropertyName("path")] + public required string Path { get; set; } +} + +/// Binary result returned by a tool for the model. +/// Nested data type for PersistedBinaryImage. +public sealed partial class PersistedBinaryImage +{ + /// Base64-encoded binary data. + [Base64String] [JsonPropertyName("data")] public required string Data { get; set; } @@ -9014,6 +10358,8 @@ public sealed partial class ToolExecutionCompleteContentText : ToolExecutionComp [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 public sealed partial class ToolExecutionCompleteContentTerminal : ToolExecutionCompleteContent { @@ -10523,6 +11869,34 @@ public override bool? ManagedApprovalRequired public string? Warning { get; set; } } +/// A named, complete UTF-8 text snapshot for a pending file operation. +/// Nested data type for PermissionWriteFileContent. +public sealed partial class PermissionWriteFileContent +{ + /// Complete file contents, including an empty string for an empty file. + [JsonPropertyName("content")] + public required string Content { get; set; } + + /// Absolute path in the session filesystem namespace. + [JsonPropertyName("path")] + public required string Path { get; set; } +} + +/// Complete text snapshots for one proposed file change. At least one side is present. Different before and after paths describe a move. +/// Nested data type for PermissionWriteFileEdit. +public sealed partial class PermissionWriteFileEdit +{ + /// Complete file content the operation plans to write. Absent only when the file is being deleted. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("after")] + public PermissionWriteFileContent? After { get; set; } + + /// File content read while planning the operation. Absent only when the file is being created. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("before")] + public PermissionWriteFileContent? Before { get; set; } +} + /// File write permission request. /// The write variant of . public sealed partial class PermissionRequestWrite : PermissionRequest @@ -10539,6 +11913,11 @@ public sealed partial class PermissionRequestWrite : PermissionRequest [JsonPropertyName("diff")] public required string Diff { get; set; } + /// Complete before/after text previews captured while planning this request. Omitted when a complete preview is unavailable, including pre-read sandbox requests and non-UTF-8 files. Missing sides mean creation or deletion, not unavailable content. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("fileEdits")] + public PermissionWriteFileEdit[]? FileEdits { get; set; } + /// Path of the file being written to. [JsonPropertyName("fileName")] public required string FileName { get; set; } @@ -11187,6 +12566,11 @@ public sealed partial class PermissionPromptRequestWrite : PermissionPromptReque [JsonPropertyName("diff")] public required string Diff { get; set; } + /// Complete before/after text previews captured while planning this request. Omitted when a complete preview is unavailable, including pre-read sandbox requests and non-UTF-8 files. Missing sides mean creation or deletion, not unavailable content. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("fileEdits")] + public PermissionWriteFileEdit[]? FileEdits { get; set; } + /// Path of the file being written to. [JsonPropertyName("fileName")] public required string FileName { get; set; } @@ -12198,6 +13582,115 @@ public sealed partial class CapabilitiesChangedUI public bool? McpApps { get; set; } } +/// The ask_user variant of . +public sealed partial class HumanResponseRecordedResponseAskUser : HumanResponseRecordedResponse +{ + /// + [JsonIgnore] + public override string ResponseKind => "ask_user"; + + /// Exact answer content accepted from the user. + [JsonPropertyName("content")] + public required IDictionary Content { get; set; } + + /// Exact question displayed to the user. + [JsonPropertyName("message")] + public required string Message { get; set; } + + /// Exact response schema displayed to the user. + [JsonPropertyName("requestedSchema")] + public required ElicitationRequestedSchema RequestedSchema { get; set; } +} + +/// The user_input variant of . +public sealed partial class HumanResponseRecordedResponseUserInput : HumanResponseRecordedResponse +{ + /// + [JsonIgnore] + public override string ResponseKind => "user_input"; + + /// Whether the displayed request allowed a free-form answer. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("allowFreeform")] + public bool? AllowFreeform { get; set; } + + /// Exact selected or free-form answer submitted by the user. + [JsonPropertyName("answer")] + public required string Answer { get; set; } + + /// Exact choices displayed to the user, when the request offered choices. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("choices")] + public string[]? Choices { get; set; } + + /// Exact question displayed to the user. + [JsonPropertyName("question")] + public required string Question { get; set; } + + /// Whether the answer was typed as free-form text rather than selected from the displayed choices. + [JsonPropertyName("wasFreeform")] + public required bool WasFreeform { get; set; } +} + +/// The exit_plan_mode variant of . +public sealed partial class HumanResponseRecordedResponseExitPlanMode : HumanResponseRecordedResponse +{ + /// + [JsonIgnore] + public override string ResponseKind => "exit_plan_mode"; + + /// Actions offered by the plan review UI. + [JsonPropertyName("actions")] + public required ExitPlanModeAction[] Actions { get; set; } + + /// Whether the user approved the reviewed plan. + [JsonPropertyName("approved")] + public required bool Approved { get; set; } + + /// Whether the selected response requested edit auto-approval. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("autoApproveEdits")] + public bool? AutoApproveEdits { get; set; } + + /// Exact feedback submitted with the plan decision, when present. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("feedback")] + public string? Feedback { get; set; } + + /// Exact full plan content available from the review UI. + [JsonPropertyName("planContent")] + public required string PlanContent { get; set; } + + /// Action the plan review UI recommended. + [JsonPropertyName("recommendedAction")] + public required ExitPlanModeAction RecommendedAction { get; set; } + + /// Action selected by the user, when applicable. + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [JsonPropertyName("selectedAction")] + public ExitPlanModeAction? SelectedAction { get; set; } + + /// Exact plan summary displayed to the user. + [JsonPropertyName("summary")] + public required string Summary { get; set; } +} + +/// Exact runtime-owned question or reviewed plan paired with the typed response that settled it. +/// Polymorphic base type discriminated by responseKind. +[JsonPolymorphic( + TypeDiscriminatorPropertyName = "responseKind", + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)] +[JsonDerivedType(typeof(HumanResponseRecordedResponseAskUser), "ask_user")] +[JsonDerivedType(typeof(HumanResponseRecordedResponseUserInput), "user_input")] +[JsonDerivedType(typeof(HumanResponseRecordedResponseExitPlanMode), "exit_plan_mode")] +public partial class HumanResponseRecordedResponse +{ + /// The type discriminator. + [JsonPropertyName("responseKind")] + public virtual string ResponseKind { get; set; } = string.Empty; +} + + /// A single resolved skill in `session.skills_loaded`, including source, invocability, enabled state, path, and argument hint. /// Nested data type for SkillsLoadedSkill. public sealed partial class SkillsLoadedSkill @@ -12467,7 +13960,7 @@ public sealed partial class McpAppToolCallCompleteToolMeta public McpAppToolCallCompleteToolMetaUI? Ui { get; set; } } -/// Routing preference used when the session model is `auto`. `fast` is an integrator-only latency preset and is not a first-party GitHub Copilot product preference. +/// Extensible routing preference for the virtual `auto` model. New identifiers must be advertised and enabled by the provider. `fast` is an integrator-only latency preset. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct AutoTier : IEquatable @@ -12508,10 +14001,10 @@ public AutoTier(string value) public override bool Equals(object? obj) => obj is AutoTier other && Equals(other); /// - public bool Equals(AutoTier other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(AutoTier other) => string.Equals(Value, other.Value, StringComparison.Ordinal); /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + public override int GetHashCode() => StringComparer.Ordinal.GetHashCode(Value); /// public override string ToString() => Value; @@ -13612,7 +15105,7 @@ public override void Write(Utf8JsonWriter writer, ModelDeselectedReason value, J } } -/// Auto preferences that Copilot API can recommend. +/// Enabled Auto preferences that Copilot API can recommend. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct RecommendedAutoTier : IEquatable @@ -13650,10 +15143,10 @@ public RecommendedAutoTier(string value) public override bool Equals(object? obj) => obj is RecommendedAutoTier other && Equals(other); /// - public bool Equals(RecommendedAutoTier other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(RecommendedAutoTier other) => string.Equals(Value, other.Value, StringComparison.Ordinal); /// - public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + public override int GetHashCode() => StringComparer.Ordinal.GetHashCode(Value); /// public override string ToString() => Value; @@ -13952,23 +15445,545 @@ public HandoffSourceType(string value) /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// The handoff originated from a remote session. - public static HandoffSourceType Remote { get; } = new("remote"); + /// The handoff originated from a remote session. + public static HandoffSourceType Remote { get; } = new("remote"); + + /// The handoff originated from a local session. + public static HandoffSourceType Local { get; } = new("local"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(HandoffSourceType left, HandoffSourceType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(HandoffSourceType left, HandoffSourceType right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is HandoffSourceType other && Equals(other); + + /// + public bool Equals(HandoffSourceType 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 HandoffSourceType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, HandoffSourceType value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(HandoffSourceType)); + } + } +} + +/// Whether the accumulated numeric AI-credit subtotal covers the observed calls. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct AiCreditsStatus : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public AiCreditsStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Every observed call supplied an AI-credit amount, including an explicit zero. + public static AiCreditsStatus Complete { get; } = new("complete"); + + /// Some calls supplied amounts and some did not; the numeric value is only a reported subtotal. + public static AiCreditsStatus Partial { get; } = new("partial"); + + /// No AI-credit amount was reported for the observed calls. Numeric zero is not a zero-cost claim. + public static AiCreditsStatus Unavailable { get; } = new("unavailable"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(AiCreditsStatus left, AiCreditsStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(AiCreditsStatus left, AiCreditsStatus right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is AiCreditsStatus other && Equals(other); + + /// + public bool Equals(AiCreditsStatus 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 AiCreditsStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, AiCreditsStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AiCreditsStatus)); + } + } +} + +/// The neutral kind of a model provider — the model analog of `AccountKind`. A model provider is the live, entitled source a model came from; central code never branches on this beyond a single dispatch. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ModelProviderKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ModelProviderKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// GitHub Copilot / CAPI models, spawned by a github-resolving account that holds a Copilot seat. + public static ModelProviderKind Copilot { get; } = new("copilot"); + + /// Microsoft 365 Copilot (Loki) inference models, spawned by a resolvable Entra-derived Loki account. + public static ModelProviderKind Loki { get; } = new("loki"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelProviderKind left, ModelProviderKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelProviderKind left, ModelProviderKind right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ModelProviderKind other && Equals(other); + + /// + public bool Equals(ModelProviderKind 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 ModelProviderKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ModelProviderKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelProviderKind)); + } + } +} + +/// Whether the session ended normally ("routine") or due to a crash/fatal error ("error"). +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ShutdownType : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ShutdownType(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The session ended normally. + public static ShutdownType Routine { get; } = new("routine"); + + /// The session ended because of a crash or fatal error. + public static ShutdownType Error { get; } = new("error"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ShutdownType left, ShutdownType right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ShutdownType left, ShutdownType right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ShutdownType other && Equals(other); + + /// + public bool Equals(ShutdownType 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 ShutdownType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ShutdownType value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ShutdownType)); + } + } +} + +/// Access policy reported by the quota service. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ProviderQuotaAccessState : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ProviderQuotaAccessState(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Access is allowed. + public static ProviderQuotaAccessState Allowed { get; } = new("allowed"); + + /// Access is denied. + public static ProviderQuotaAccessState Denied { get; } = new("denied"); + + /// Quota is not required for this access. + public static ProviderQuotaAccessState NotRequired { get; } = new("not_required"); + + /// The service did not supply a recognized access state. + public static ProviderQuotaAccessState Unknown { get; } = new("unknown"); + + /// Client-only compatibility placeholder when acquisitionStatus is unavailable or failed; not an observed service verdict. + public static ProviderQuotaAccessState Unavailable { get; } = new("unavailable"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderQuotaAccessState left, ProviderQuotaAccessState right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderQuotaAccessState left, ProviderQuotaAccessState right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ProviderQuotaAccessState other && Equals(other); + + /// + public bool Equals(ProviderQuotaAccessState 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 ProviderQuotaAccessState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ProviderQuotaAccessState value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaAccessState)); + } + } +} + +/// Acquisition state, independent of the service's business access/capacity verdict. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ProviderQuotaAcquisitionStatus : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ProviderQuotaAcquisitionStatus(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// A service quota observation was received. + public static ProviderQuotaAcquisitionStatus Succeeded { get; } = new("succeeded"); + + /// No service snapshot is available for this caller or surface. + public static ProviderQuotaAcquisitionStatus Unavailable { get; } = new("unavailable"); + + /// Acquisition failed; inspect httpStatus/acquisitionError without inferring a business verdict. + public static ProviderQuotaAcquisitionStatus Failed { get; } = new("failed"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderQuotaAcquisitionStatus left, ProviderQuotaAcquisitionStatus right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderQuotaAcquisitionStatus left, ProviderQuotaAcquisitionStatus right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ProviderQuotaAcquisitionStatus other && Equals(other); + + /// + public bool Equals(ProviderQuotaAcquisitionStatus 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 ProviderQuotaAcquisitionStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ProviderQuotaAcquisitionStatus value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaAcquisitionStatus)); + } + } +} + +/// Capacity is independent of whether a numeric balance was supplied. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ProviderQuotaCapacityState : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ProviderQuotaCapacityState(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Capacity is available. + public static ProviderQuotaCapacityState Available { get; } = new("available"); + + /// The service explicitly reports exhaustion. + public static ProviderQuotaCapacityState Exhausted { get; } = new("exhausted"); + + /// The reported entitlement is unlimited. + public static ProviderQuotaCapacityState Unlimited { get; } = new("unlimited"); + + /// No billable quota is required. + public static ProviderQuotaCapacityState NotRequired { get; } = new("not_required"); + + /// The service reports that capacity is not applicable. + public static ProviderQuotaCapacityState NotApplicable { get; } = new("not_applicable"); + + /// No recognized capacity state was supplied. + public static ProviderQuotaCapacityState Unknown { get; } = new("unknown"); + + /// Client-only compatibility placeholder when acquisitionStatus is unavailable or failed; not an observed service verdict. + public static ProviderQuotaCapacityState Unavailable { get; } = new("unavailable"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderQuotaCapacityState left, ProviderQuotaCapacityState right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderQuotaCapacityState left, ProviderQuotaCapacityState right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ProviderQuotaCapacityState other && Equals(other); + + /// + public bool Equals(ProviderQuotaCapacityState 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 ProviderQuotaCapacityState Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ProviderQuotaCapacityState value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaCapacityState)); + } + } +} + +/// Timing and purpose of a provider quota observation. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ProviderQuotaObservationKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ProviderQuotaObservationKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// An explicit account read; consumption may still be subject to service aggregation delay. + public static ProviderQuotaObservationKind AccountSnapshot { get; } = new("account_snapshot"); + + /// A verdict attached before an inference response. May be cached; never a post-charge balance. + public static ProviderQuotaObservationKind AdmissionState { get; } = new("admission_state"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderQuotaObservationKind left, ProviderQuotaObservationKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderQuotaObservationKind left, ProviderQuotaObservationKind right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ProviderQuotaObservationKind other && Equals(other); + + /// + public bool Equals(ProviderQuotaObservationKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + + /// + public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + + /// + public override string ToString() => Value; + + /// Provides a for serializing instances. + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class Converter : JsonConverter + { + /// + public override ProviderQuotaObservationKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ProviderQuotaObservationKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaObservationKind)); + } + } +} + +/// Interpretation permitted for independently reported quantities. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ProviderQuotaQuantityKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ProviderQuotaQuantityKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Quantities form an authoritative available-of-entitled budget suitable for percentage presentation. + public static ProviderQuotaQuantityKind AuthoritativeBudget { get; } = new("authoritative_budget"); + + /// Raw advisory quantities; do not infer permission, usage percentage, reset cadence, or post-charge balance. + public static ProviderQuotaQuantityKind AdvisoryBalance { get; } = new("advisory_balance"); - /// The handoff originated from a local session. - public static HandoffSourceType Local { get; } = new("local"); + /// No quantity measurement, as with admission-only response headers. + public static ProviderQuotaQuantityKind None { get; } = new("none"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(HandoffSourceType left, HandoffSourceType right) => left.Equals(right); + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderQuotaQuantityKind left, ProviderQuotaQuantityKind right) => left.Equals(right); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(HandoffSourceType left, HandoffSourceType right) => !(left == right); + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderQuotaQuantityKind left, ProviderQuotaQuantityKind right) => !(left == right); /// - public override bool Equals(object? obj) => obj is HandoffSourceType other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderQuotaQuantityKind other && Equals(other); /// - public bool Equals(HandoffSourceType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderQuotaQuantityKind other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -13976,60 +15991,66 @@ public HandoffSourceType(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 HandoffSourceType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderQuotaQuantityKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, HandoffSourceType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderQuotaQuantityKind value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(HandoffSourceType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaQuantityKind)); } } } -/// Whether the session ended normally ("routine") or due to a crash/fatal error ("error"). +/// Units explicitly reported by a quota provider. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] -public readonly struct ShutdownType : IEquatable +public readonly struct ProviderQuotaUnit : 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 ShutdownType(string value) + public ProviderQuotaUnit(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 ended normally. - public static ShutdownType Routine { get; } = new("routine"); + /// AI credits, not tokens or currency. + public static ProviderQuotaUnit AiCredits { get; } = new("ai_credits"); - /// The session ended because of a crash or fatal error. - public static ShutdownType Error { get; } = new("error"); + /// Requests charged against an entitlement. + public static ProviderQuotaUnit Requests { get; } = new("requests"); - /// Returns a value indicating whether two instances are equivalent. - public static bool operator ==(ShutdownType left, ShutdownType right) => left.Equals(right); + /// Model tokens. + public static ProviderQuotaUnit Tokens { get; } = new("tokens"); - /// Returns a value indicating whether two instances are not equivalent. - public static bool operator !=(ShutdownType left, ShutdownType right) => !(left == right); + /// The observation does not establish a quantity unit. + public static ProviderQuotaUnit Unknown { get; } = new("unknown"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ProviderQuotaUnit left, ProviderQuotaUnit right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ProviderQuotaUnit left, ProviderQuotaUnit right) => !(left == right); /// - public override bool Equals(object? obj) => obj is ShutdownType other && Equals(other); + public override bool Equals(object? obj) => obj is ProviderQuotaUnit other && Equals(other); /// - public bool Equals(ShutdownType other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + public bool Equals(ProviderQuotaUnit other) => string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); /// public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Value); @@ -14037,20 +16058,20 @@ public ShutdownType(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 ShutdownType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override ProviderQuotaUnit Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); } /// - public override void Write(Utf8JsonWriter writer, ShutdownType value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, ProviderQuotaUnit value, JsonSerializerOptions options) { - GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ShutdownType)); + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ProviderQuotaUnit)); } } } @@ -16078,6 +18099,76 @@ public override void Write(Utf8JsonWriter writer, AssistantUsageTransport value, } } +/// Why a WebSocket-capable model call was carried by the HTTP fallback. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ModelCallWebSocketFallbackReason : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ModelCallWebSocketFallbackReason(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// The WebSocket connection could not be established. + public static ModelCallWebSocketFallbackReason ConnectFailed { get; } = new("connect_failed"); + + /// No usable WebSocket connection was available for the request. + public static ModelCallWebSocketFallbackReason ConnectionUnavailable { get; } = new("connection_unavailable"); + + /// Sending the request over the WebSocket failed. + public static ModelCallWebSocketFallbackReason SendFailed { get; } = new("send_failed"); + + /// The WebSocket returned a retryable API error. + public static ModelCallWebSocketFallbackReason ApiError { get; } = new("api_error"); + + /// The WebSocket transport failed before any output reached the consumer. + public static ModelCallWebSocketFallbackReason TransportFailed { get; } = new("transport_failed"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ModelCallWebSocketFallbackReason left, ModelCallWebSocketFallbackReason right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ModelCallWebSocketFallbackReason left, ModelCallWebSocketFallbackReason right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ModelCallWebSocketFallbackReason other && Equals(other); + + /// + public bool Equals(ModelCallWebSocketFallbackReason 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 ModelCallWebSocketFallbackReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ModelCallWebSocketFallbackReason value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelCallWebSocketFallbackReason)); + } + } +} + /// For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -16477,7 +18568,7 @@ public override void Write(Utf8JsonWriter writer, AbortReason value, JsonSeriali } } -/// Configuration source: user, workspace, plugin, builtin, or managed. +/// Configuration source: user, workspace, plugin, builtin, managed, or account. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct McpServerSource : IEquatable @@ -16511,6 +18602,9 @@ public McpServerSource(string value) /// Server supplied by a trusted host-managed catalog. public static McpServerSource Managed { get; } = new("managed"); + /// Server contributed by a signed-in account; enablement and organization policy still apply. + public static McpServerSource Account { get; } = new("account"); + /// Returns a value indicating whether two instances are equivalent. public static bool operator ==(McpServerSource left, McpServerSource right) => left.Equals(right); @@ -16675,6 +18769,135 @@ public override void Write(Utf8JsonWriter writer, ToolExecutionStartToolDescript } } +/// Shell output source. Terminal output has no separate stdout/stderr attribution. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ToolShellOutputStream : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ToolShellOutputStream(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Output from the shell command's standard output stream. This is the default when stream is omitted. + public static ToolShellOutputStream Stdout { get; } = new("stdout"); + + /// Output from the shell command's standard error stream. + public static ToolShellOutputStream Stderr { get; } = new("stderr"); + + /// Inherently merged output that cannot be attributed separately to stdout or stderr. + public static ToolShellOutputStream Terminal { get; } = new("terminal"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ToolShellOutputStream left, ToolShellOutputStream right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ToolShellOutputStream left, ToolShellOutputStream right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ToolShellOutputStream other && Equals(other); + + /// + public bool Equals(ToolShellOutputStream 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 ToolShellOutputStream Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ToolShellOutputStream value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ToolShellOutputStream)); + } + } +} + +/// Kind of file mutation committed by a built-in editing tool. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ToolExecutionCompleteFileEditKind : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ToolExecutionCompleteFileEditKind(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// A file was created. + public static ToolExecutionCompleteFileEditKind Create { get; } = new("create"); + + /// A file was written by an edit operation. + public static ToolExecutionCompleteFileEditKind Edit { get; } = new("edit"); + + /// A file was deleted. + public static ToolExecutionCompleteFileEditKind Delete { get; } = new("delete"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ToolExecutionCompleteFileEditKind left, ToolExecutionCompleteFileEditKind right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ToolExecutionCompleteFileEditKind left, ToolExecutionCompleteFileEditKind right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ToolExecutionCompleteFileEditKind other && Equals(other); + + /// + public bool Equals(ToolExecutionCompleteFileEditKind 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 ToolExecutionCompleteFileEditKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ToolExecutionCompleteFileEditKind value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ToolExecutionCompleteFileEditKind)); + } + } +} + /// Binary result type discriminator. Use "image" for images and "resource" for other binary data. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -18711,6 +20934,9 @@ public PermissionApprovalEvaluationReasonCode(string value) /// The script path was not authorized for inspection. public static PermissionApprovalEvaluationReasonCode PathNotAuthorized { get; } = new("path-not-authorized"); + /// A code source was excluded from review by content exclusion policy. + public static PermissionApprovalEvaluationReasonCode ContentExcluded { get; } = new("content-excluded"); + /// The script working directory was invalid. public static PermissionApprovalEvaluationReasonCode InvalidWorkingDirectory { get; } = new("invalid-working-directory"); @@ -18747,6 +20973,24 @@ public PermissionApprovalEvaluationReasonCode(string value) /// The script argument binding could not be reviewed. public static PermissionApprovalEvaluationReasonCode ArgumentBindingUnreviewable { get; } = new("argument-binding-unreviewable"); + /// The shell command could not be analyzed for execution evidence. + public static PermissionApprovalEvaluationReasonCode UnsupportedCommandShape { get; } = new("unsupported-command-shape"); + + /// The shell command used a code source that cannot be bound for review. + public static PermissionApprovalEvaluationReasonCode UnsupportedSource { get; } = new("unsupported-source"); + + /// The shell command used a code source computed at run time. + public static PermissionApprovalEvaluationReasonCode DynamicSource { get; } = new("dynamic-source"); + + /// The shell command referenced more code sources than can be reviewed. + public static PermissionApprovalEvaluationReasonCode TooManySources { get; } = new("too-many-sources"); + + /// A code-bearing executable could not be inspected. + public static PermissionApprovalEvaluationReasonCode ExecutableUnavailable { get; } = new("executable-unavailable"); + + /// A code-bearing executable exceeded the binding size limit. + public static PermissionApprovalEvaluationReasonCode ExecutableTooLarge { get; } = new("executable-too-large"); + /// The script review metadata was malformed. public static PermissionApprovalEvaluationReasonCode MalformedScriptActionReview { get; } = new("malformed-script-action-review"); @@ -19969,6 +22213,74 @@ public override void Write(Utf8JsonWriter writer, AutoModeResolvedReasoningBucke } } +/// Current phase of organization-required plugin preparation. +[Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct ManagedPluginProgressPhase : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public ManagedPluginProgressPhase(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// Managed settings are still resolving before plugin preparation can be planned. + public static ManagedPluginProgressPhase Initializing { get; } = new("initializing"); + + /// Required plugin content is being installed. + public static ManagedPluginProgressPhase Installing { get; } = new("installing"); + + /// Installed required plugin content is being updated. + public static ManagedPluginProgressPhase Updating { get; } = new("updating"); + + /// Managed plugin preparation has completed. + public static ManagedPluginProgressPhase Complete { get; } = new("complete"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(ManagedPluginProgressPhase left, ManagedPluginProgressPhase right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(ManagedPluginProgressPhase left, ManagedPluginProgressPhase right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is ManagedPluginProgressPhase other && Equals(other); + + /// + public bool Equals(ManagedPluginProgressPhase 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 ManagedPluginProgressPhase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, ManagedPluginProgressPhase value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ManagedPluginProgressPhase)); + } + } +} + /// Summary of which managed-settings channels contributed to the effective session policy. Use the per-channel booleans for exact provenance. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -20061,7 +22373,7 @@ public ManagedSettingsEnforcedAction(string value) /// Gets the value associated with this . public string Value => _value ?? string.Empty; - /// An attempt to turn on a bypass-permissions ("yolo") escalation was refused or capped because policy disables bypass-permissions mode. + /// An attempt to enter a permission mode governed by managed policy was refused or capped. The `setting` and `escalation` fields identify whether this was bypass permissions or Assisted Permissions. public static ManagedSettingsEnforcedAction BypassPermissionsBlocked { get; } = new("bypass_permissions_blocked"); /// Returns a value indicating whether two instances are equivalent. @@ -20100,7 +22412,7 @@ public override void Write(Utf8JsonWriter writer, ManagedSettingsEnforcedAction } } -/// For a `bypass_permissions_blocked` action, which permission-escalation primitive was refused. +/// For a `bypass_permissions_blocked` action, which permission-mode or escalation primitive was refused. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] public readonly struct ManagedSettingsEnforcedEscalation : IEquatable @@ -20125,7 +22437,7 @@ public ManagedSettingsEnforcedEscalation(string value) /// Automatic approval of all tool permission requests. public static ManagedSettingsEnforcedEscalation ApproveAll { get; } = new("approve_all"); - /// Assisted mode — keeps normal prompt paths and adds an LLM recommendation, distinct from allow-all. + /// Assisted Permissions — uses an LLM review to reduce prompts, distinct from allow-all and not a hard security boundary. public static ManagedSettingsEnforcedEscalation AssistedApproval { get; } = new("assisted_approval"); /// Unrestricted filesystem access outside the session's allowed directories. @@ -20240,6 +22552,70 @@ public override void Write(Utf8JsonWriter writer, ExitPlanModeAction value, Json } } +/// Controlled provenance for a typed runtime response. Only `human_response`, minted by a trusted direct-interaction ingress, is human authorization evidence. +[JsonConverter(typeof(Converter))] +[DebuggerDisplay("{Value,nq}")] +public readonly struct HumanResponseActor : IEquatable +{ + private readonly string? _value; + + /// Initializes a new instance of the struct. + /// The value to associate with this . + [JsonConstructor] + public HumanResponseActor(string value) + { + ArgumentException.ThrowIfNullOrWhiteSpace(value); + _value = value; + } + + /// Gets the value associated with this . + public string Value => _value ?? string.Empty; + + /// A built-in trusted client submitted the response after direct human interaction. + public static HumanResponseActor HumanResponse { get; } = new("human_response"); + + /// A host or SDK automation submitted the response without direct human interaction. + public static HumanResponseActor HostAutomation { get; } = new("host_automation"); + + /// The response came through a legacy or otherwise unattributed ingress. + public static HumanResponseActor Unknown { get; } = new("unknown"); + + /// Returns a value indicating whether two instances are equivalent. + public static bool operator ==(HumanResponseActor left, HumanResponseActor right) => left.Equals(right); + + /// Returns a value indicating whether two instances are not equivalent. + public static bool operator !=(HumanResponseActor left, HumanResponseActor right) => !(left == right); + + /// + public override bool Equals(object? obj) => obj is HumanResponseActor other && Equals(other); + + /// + public bool Equals(HumanResponseActor 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 HumanResponseActor Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert)); + } + + /// + public override void Write(Utf8JsonWriter writer, HumanResponseActor value, JsonSerializerOptions options) + { + GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(HumanResponseActor)); + } + } +} + /// Terminal status a workflow run committed. A settled run is never `pending` or `running`, so those two members of the run-status domain are deliberately absent. [JsonConverter(typeof(Converter))] [DebuggerDisplay("{Value,nq}")] @@ -20484,7 +22860,7 @@ public McpServerStatus(string value) /// The server is configured but disabled. public static McpServerStatus Disabled { get; } = new("disabled"); - /// The server was intentionally stopped and can be restarted on demand when policy permits; a server quarantined by restrictive managed policy stays stopped and cannot be restarted until the policy allows it. + /// The server is not running: it may not have started yet, may have been explicitly stopped, or may be quarantined by restrictive managed policy. It can be restarted on demand when policy permits. public static McpServerStatus Stopped { get; } = new("stopped"); /// The server is not configured for this session. @@ -20802,6 +23178,12 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(HookProgressEvent))] [JsonSerializable(typeof(HookStartData))] [JsonSerializable(typeof(HookStartEvent))] +[JsonSerializable(typeof(HumanResponseRecordedData))] +[JsonSerializable(typeof(HumanResponseRecordedEvent))] +[JsonSerializable(typeof(HumanResponseRecordedResponse))] +[JsonSerializable(typeof(HumanResponseRecordedResponseAskUser))] +[JsonSerializable(typeof(HumanResponseRecordedResponseExitPlanMode))] +[JsonSerializable(typeof(HumanResponseRecordedResponseUserInput))] [JsonSerializable(typeof(McpAppToolCallCompleteData))] [JsonSerializable(typeof(McpAppToolCallCompleteError))] [JsonSerializable(typeof(McpAppToolCallCompleteEvent))] @@ -20835,6 +23217,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(ModelCallFinishedEvent))] [JsonSerializable(typeof(ModelCallStartData))] [JsonSerializable(typeof(ModelCallStartEvent))] +[JsonSerializable(typeof(ModelProviderRef))] [JsonSerializable(typeof(OmittedBinaryResult))] [JsonSerializable(typeof(PendingMessagesModifiedData))] [JsonSerializable(typeof(PendingMessagesModifiedEvent))] @@ -20901,10 +23284,14 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(PermissionResultDeniedNoApprovalRuleAndCouldNotRequestFromUser))] [JsonSerializable(typeof(PermissionRule))] [JsonSerializable(typeof(PermissionSandboxPathGrant))] +[JsonSerializable(typeof(PermissionWriteFileContent))] +[JsonSerializable(typeof(PermissionWriteFileEdit))] [JsonSerializable(typeof(PersistedBinaryImage))] [JsonSerializable(typeof(PersistedBinaryResult))] [JsonSerializable(typeof(PromptCacheBreakData))] [JsonSerializable(typeof(PromptCacheBreakEvent))] +[JsonSerializable(typeof(ProviderQuotaBudgetMetadata))] +[JsonSerializable(typeof(ProviderQuotaState))] [JsonSerializable(typeof(ResponsesReasoning))] [JsonSerializable(typeof(SamplingCompletedData))] [JsonSerializable(typeof(SamplingCompletedEvent))] @@ -20962,6 +23349,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(SessionErrorData))] [JsonSerializable(typeof(SessionErrorEvent))] [JsonSerializable(typeof(SessionEvent))] +[JsonSerializable(typeof(SessionEventEnvelope))] [JsonSerializable(typeof(SessionExtensionsAttachmentsPushedData))] [JsonSerializable(typeof(SessionExtensionsAttachmentsPushedEvent))] [JsonSerializable(typeof(SessionExtensionsLoadedData))] @@ -20992,6 +23380,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(SessionLimitsExhaustedRequestedData))] [JsonSerializable(typeof(SessionLimitsExhaustedRequestedEvent))] [JsonSerializable(typeof(SessionLimitsExhaustedResponse))] +[JsonSerializable(typeof(SessionManagedPluginProgressData))] +[JsonSerializable(typeof(SessionManagedPluginProgressEvent))] [JsonSerializable(typeof(SessionManagedSettingsEnforcedData))] [JsonSerializable(typeof(SessionManagedSettingsEnforcedEvent))] [JsonSerializable(typeof(SessionManagedSettingsResolvedData))] @@ -21018,6 +23408,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(SessionPermissionsChangedEvent))] [JsonSerializable(typeof(SessionPlanChangedData))] [JsonSerializable(typeof(SessionPlanChangedEvent))] +[JsonSerializable(typeof(SessionQuotaObservationData))] +[JsonSerializable(typeof(SessionQuotaObservationEvent))] [JsonSerializable(typeof(SessionRemoteSteerableChangedData))] [JsonSerializable(typeof(SessionRemoteSteerableChangedEvent))] [JsonSerializable(typeof(SessionResumeData))] @@ -21116,6 +23508,7 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(ToolExecutionCompleteData))] [JsonSerializable(typeof(ToolExecutionCompleteError))] [JsonSerializable(typeof(ToolExecutionCompleteEvent))] +[JsonSerializable(typeof(ToolExecutionCompleteFileEdit))] [JsonSerializable(typeof(ToolExecutionCompleteResult))] [JsonSerializable(typeof(ToolExecutionCompleteShellExecution))] [JsonSerializable(typeof(ToolExecutionCompleteToolDescription))] @@ -21142,11 +23535,23 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu [JsonSerializable(typeof(ToolExecutionStartToolDescriptionMetaUI))] [JsonSerializable(typeof(ToolSearchActivatedData))] [JsonSerializable(typeof(ToolSearchActivatedEvent))] +[JsonSerializable(typeof(ToolShellOutputData))] +[JsonSerializable(typeof(ToolShellOutputEvent))] [JsonSerializable(typeof(ToolUserRequestedData))] [JsonSerializable(typeof(ToolUserRequestedEvent))] [JsonSerializable(typeof(UiEphemeralQueryData))] [JsonSerializable(typeof(UiEphemeralQueryEvent))] +[JsonSerializable(typeof(UsageAccountingIdentity))] [JsonSerializable(typeof(UsageCheckpointModelCacheState))] +[JsonSerializable(typeof(UsageGetMetricsResult))] +[JsonSerializable(typeof(UsageMetricsAgentMetric))] +[JsonSerializable(typeof(UsageMetricsCodeChanges))] +[JsonSerializable(typeof(UsageMetricsModelMetric))] +[JsonSerializable(typeof(UsageMetricsModelMetricRequests))] +[JsonSerializable(typeof(UsageMetricsModelMetricTokenDetail))] +[JsonSerializable(typeof(UsageMetricsModelMetricUsage))] +[JsonSerializable(typeof(UsageMetricsProviderModelMetric))] +[JsonSerializable(typeof(UsageMetricsTokenDetail))] [JsonSerializable(typeof(UserInputCompletedData))] [JsonSerializable(typeof(UserInputCompletedEvent))] [JsonSerializable(typeof(UserInputRequestedData))] diff --git a/dotnet/src/Host.cs b/dotnet/src/Host.cs index 5835ea3a4c..9ce98c3212 100644 --- a/dotnet/src/Host.cs +++ b/dotnet/src/Host.cs @@ -39,6 +39,8 @@ public sealed record AhpSessionResumeRequest(string SessionId, ResumeSessionConf [Experimental(Diagnostics.Experimental)] public sealed class AhpHostOptions { + /// Stable durable catalog identity; must agree with GitHubEnvironment.ComputeId when both are supplied. + public string? ComputeId { get; init; } /// Local WebSocket transport settings; absent disables the local listener. public HostLocalServerOptions? LocalServer { get; init; } /// GitHub Mission Control transport settings, with required Name and ComputeId; absent disables it. @@ -84,10 +86,14 @@ internal AhpHost(HostStartResult info, ServerHostApi rpc) /// The GitHub Mission Control environment ID, absent without a GitHub environment. public string? EnvironmentId { get; } - /// Publishes a resident session without invoking factories or transferring ownership. + /// Durably advertises an attached session without invoking factories or transferring ownership. public Task PublishSessionAsync(string sessionId, CancellationToken cancellationToken = default) => _rpc.PublishSessionAsync(HostId, sessionId, cancellationToken); + /// Lists all live and dormant catalog sessions advertised by this host. + public Task ListSessionsAsync(CancellationToken cancellationToken = default) + => _rpc.ListSessionsAsync(HostId, cancellationToken); + /// Stops and joins listener cleanup without deleting application sessions. public ValueTask DisposeAsync() => new(DisposeAsync(CancellationToken.None)); @@ -121,7 +127,8 @@ public async Task StartAhpHostAsync(AhpHostOptions options, Cancellatio var githubEnvironment = options.GitHubEnvironment is { } github ? new HostGitHubEnvironmentOptions { Name = github.Name, - ComputeId = github.ComputeId + ComputeId = github.ComputeId, + RequireConnectionBinding = github.RequireConnectionBinding } : null; if (localServer is null && githubEnvironment is null) throw new ArgumentException("At least one AHP transport must be configured", nameof(options)); @@ -130,9 +137,15 @@ public async Task StartAhpHostAsync(AhpHostOptions options, Cancellatio var hostId = Guid.NewGuid().ToString(); lock (_ahpGate) _ahpHosts.Add(hostId, options); var rpc = connection.Server.Host; - var startup = rpc.StartAsync(hostId, localServer, githubEnvironment, - options.CreateSession is not null ? true : null, - options.ResumeSession is not null ? true : null, CancellationToken.None); + var startup = rpc.StartAsync(new HostStartRequest + { + HostId = hostId, + ComputeId = options.ComputeId, + LocalServer = localServer, + GitHubEnvironment = githubEnvironment, + SessionFactory = options.CreateSession is not null ? true : null, + ResumeFactory = options.ResumeSession is not null ? true : null + }, CancellationToken.None); try { var info = await startup.WaitAsync(Timeout.InfiniteTimeSpan, cancellationToken).ConfigureAwait(false); diff --git a/dotnet/src/JsonRpc.cs b/dotnet/src/JsonRpc.cs index 26d4fe297a..8d45eb6f4a 100644 --- a/dotnet/src/JsonRpc.cs +++ b/dotnet/src/JsonRpc.cs @@ -1039,11 +1039,12 @@ private sealed class IncomingRequestCancellation : IDisposable public IncomingRequestCancellation(long id, CancellationToken connectionClosedToken) { Id = id; - _combinedSource = CancellationTokenSource.CreateLinkedTokenSource(_requestSource.Token, connectionClosedToken); _registration = _requestSource.Token.Register(static state => { ((TaskCompletionSource)state!).TrySetResult(); }, _requestCancelled); + // Cancellation callbacks run in reverse registration order: propagate to handlers before replying. + _combinedSource = CancellationTokenSource.CreateLinkedTokenSource(_requestSource.Token, connectionClosedToken); } public long Id { get; } diff --git a/dotnet/src/Session.StructuredOutput.cs b/dotnet/src/Session.StructuredOutput.cs index 6df54d121e..c190c13ee3 100644 --- a/dotnet/src/Session.StructuredOutput.cs +++ b/dotnet/src/Session.StructuredOutput.cs @@ -192,10 +192,10 @@ void ProcessEvent(SessionEvent evt) } pendingEvents.Clear(); } - await Task.WhenAny(completion.Task, JsonRpc.Completion, _eventChannel.Reader.Completion); + await Task.WhenAny(completion.Task, _eventDispatchStopped.Task, JsonRpc.Completion, _eventChannel.Reader.Completion); if (!completion.Task.IsCompleted) { - throw new IOException("The session closed before a final structured response was received."); + completion.TrySetException(new IOException("The session closed before a final structured response was received.")); } return await completion.Task; } diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs index acdd434b84..79afecd20b 100644 --- a/dotnet/src/Session.cs +++ b/dotnet/src/Session.cs @@ -58,9 +58,11 @@ namespace GitHub.Copilot; /// public sealed partial class CopilotSession : IAsyncDisposable { - private readonly Dictionary _toolHandlers = []; - private readonly Dictionary> _commandHandlers = []; - private readonly Dictionary>> _bearerTokenProviders = new(StringComparer.Ordinal); + private IReadOnlyDictionary _toolHandlers = new Dictionary(StringComparer.Ordinal); + private Dictionary> _commandHandlers = new(StringComparer.Ordinal); + private IReadOnlyDictionary>> _bearerTokenProviders = new Dictionary>>(StringComparer.Ordinal); + private readonly object _callbackLock = new(); + private bool _callbacksRetired; private readonly ConcurrentDictionary _pendingExternalTools = new(StringComparer.Ordinal); private readonly CancellationTokenSource _externalToolLifetime = new(); private readonly ILogger _logger; @@ -73,15 +75,29 @@ public sealed partial class CopilotSession : IAsyncDisposable private volatile Func>? _elicitationHandler; private volatile Func>? _exitPlanModeHandler; private volatile Func>? _autoModeSwitchHandler; + private volatile ISkillProvider? _skillProvider; private ImmutableArray _eventHandlers = ImmutableArray.Empty; - private sealed record EventSubscription(Type EventType, Action Handler, bool RootAgentOnly); + private sealed class EventSubscription(Type eventType, Action handler, bool rootAgentOnly) + { + private Action? _handler = handler; + + public Type EventType { get; } = eventType; + public bool RootAgentOnly { get; } = rootAgentOnly; + + // Keep the user handler out of the retained unsubscribe callback's closure. + public static EventSubscription Create(Action handler, bool rootAgentOnly) where T : SessionEvent => + new(typeof(T), evt => handler((T)evt), rootAgentOnly); + + public void Invoke(SessionEvent sessionEvent) => Volatile.Read(ref _handler)?.Invoke(sessionEvent); + + public void ClearHandler() => Interlocked.Exchange(ref _handler, null); + } private SessionHooks? _hooks; - private readonly SemaphoreSlim _hooksLock = new(1, 1); + private readonly SemaphoreSlim _setToolsLock = new(1, 1); private Dictionary>>? _transformCallbacks; - private readonly SemaphoreSlim _transformCallbacksLock = new(1, 1); private IReadOnlyList _openCanvases = Array.Empty(); @@ -92,9 +108,10 @@ private sealed record EventSubscription(Type EventType, Action Han /// Channel that serializes event dispatch. enqueues; /// a single background consumer () dequeues and /// invokes handlers one at a time, preserving arrival order. + /// Shutdown can drain concurrently without waiting for an in-flight handler. /// - private readonly Channel _eventChannel = Channel.CreateUnbounded( - new() { SingleReader = true }); + private readonly Channel _eventChannel = Channel.CreateUnbounded(); + private readonly TaskCompletionSource _eventDispatchStopped = new(TaskCreationOptions.RunContinuationsAsynchronously); /// /// Fixed name of the runtime's built-in tool-search tool. A client can @@ -212,19 +229,56 @@ internal void RemoveFromClient() } /// - /// Stops the session's event consumer () without - /// making an RPC. and - /// use this on error paths where a - /// locally registered session fails before it can be returned to the caller: - /// starts the consumer eagerly, and no caller - /// ever receives the failed session to dispose it. Safe to call more than once — - /// is idempotent. + /// Retires the event consumer without making an RPC or joining an admitted handler. + /// Releases queued events and subscriptions and fails pending response waiters. + /// Also used for registered sessions that fail before being returned to the caller. + /// Safe to call more than once. /// internal void CloseEventChannel() { + _eventDispatchStopped.TrySetResult(true); _eventChannel.Writer.TryComplete(); + while (_eventChannel.Reader.TryRead(out _)) + { + // Discard queued events; retirement must not dispatch their handlers. + } + var subscriptions = ImmutableInterlocked.InterlockedExchange(ref _eventHandlers, ImmutableArray.Empty); + foreach (var subscription in subscriptions) + { + subscription.ClearHandler(); + } + } + + /// Releases local callback ownership without an RPC or joining admitted handlers. + internal void Retire() + { + lock (_callbackLock) + { + _callbacksRetired = true; + Volatile.Write(ref _toolHandlers, new Dictionary(StringComparer.Ordinal)); + _commandHandlers = new Dictionary>(StringComparer.Ordinal); + _bearerTokenProviders = new Dictionary>>(StringComparer.Ordinal); + _permissionHandler = null; + _mcpAuthHandler = null; + _userInputHandler = null; + _elicitationHandler = null; + _exitPlanModeHandler = null; + _autoModeSwitchHandler = null; + _skillProvider = null; + _hooks = null; + _transformCallbacks = null; + ClientSessionApis.ProviderToken = null; + ClientSessionApis.Canvas = null; + ClientSessionApis.SessionFs = null; + } + CancelPendingExternalTools(); + CloseEventChannel(); + ReleaseGitHubTokenProviderRegistration(); } + private void ThrowIfCallbacksRetired() => + ObjectDisposedException.ThrowIf(_callbacksRetired, this); + /// /// Removes the session from its parent client and stops its event consumer. /// Used on session-creation/resume error paths where the session was registered @@ -232,8 +286,7 @@ internal void CloseEventChannel() /// internal void Unregister() { - CancelPendingExternalTools(); - CloseEventChannel(); + Retire(); RemoveFromClient(); } @@ -368,6 +421,7 @@ public async Task SendAsync(MessageOptions options, CancellationToken ca /// Thrown if the timeout is reached before the session becomes idle. /// Thrown if the is cancelled. /// Thrown if the session has been disposed. + /// Thrown if session event delivery stops before a final response arrives. /// /// /// This is a convenience method that combines with waiting for @@ -453,6 +507,11 @@ void Handler(SessionEvent evt) }); try { + await Task.WhenAny(tcs.Task, _eventDispatchStopped.Task); + if (!tcs.Task.IsCompleted) + { + tcs.TrySetException(new IOException("The session closed before a final response was received.")); + } var result = await tcs.Task; LoggingHelpers.LogTiming(_logger, LogLevel.Debug, null, "CopilotSession.SendAndWaitAsync complete. Elapsed={Elapsed}, SessionId={SessionId}, CompletedBy={CompletedBy}, AssistantMessageReceived={AssistantMessageReceived}", @@ -487,6 +546,8 @@ void Handler(SessionEvent evt) /// /// A callback to be invoked when a session event occurs. /// An that, when disposed, unsubscribes the handler. + /// Thrown if the session has been disposed. + /// Thrown if session event delivery has stopped. /// /// /// Events include assistant messages, tool executions, errors, and session state changes. @@ -497,6 +558,10 @@ void Handler(SessionEvent evt) /// A handler will never be called concurrently with itself or with other handlers /// on the same session. /// + /// + /// Unsubscribing or closing the session releases handlers that have not been admitted + /// for dispatch. A handler already admitted may finish; disposal does not wait for it. + /// /// /// /// @@ -528,9 +593,20 @@ private ActionDisposable OnCore(Action handler, bool rootAgentOnly) where ArgumentNullException.ThrowIfNull(handler); ThrowIfDisposed(); - var subscription = new EventSubscription(typeof(T), evt => handler((T)evt), rootAgentOnly); + var subscription = EventSubscription.Create(handler, rootAgentOnly); ImmutableInterlocked.Update(ref _eventHandlers, array => array.Add(subscription)); - return new ActionDisposable(() => ImmutableInterlocked.Update(ref _eventHandlers, array => array.Remove(subscription))); + if (_eventDispatchStopped.Task.IsCompleted) + { + subscription.ClearHandler(); + ImmutableInterlocked.Update(ref _eventHandlers, array => array.Remove(subscription)); + ThrowIfDisposed(); + throw new IOException("The session event dispatch has stopped."); + } + return new ActionDisposable(() => + { + subscription.ClearHandler(); + ImmutableInterlocked.Update(ref _eventHandlers, array => array.Remove(subscription)); + }); } /// @@ -566,12 +642,20 @@ private async Task ProcessEventsAsync() { await foreach (var sessionEvent in _eventChannel.Reader.ReadAllAsync()) { + if (_eventDispatchStopped.Task.IsCompleted) + { + return; + } var dispatchTimestamp = Stopwatch.GetTimestamp(); var eventType = sessionEvent.GetType(); // Preserve wire attribution without moving waiters ahead of earlier user handlers. var isRootAgentEvent = string.IsNullOrEmpty(sessionEvent.AgentId); foreach (var subscription in _eventHandlers) { + if (_eventDispatchStopped.Task.IsCompleted) + { + return; + } if (!subscription.EventType.IsAssignableFrom(eventType) || (subscription.RootAgentOnly && !isRootAgentEvent)) { @@ -579,7 +663,7 @@ private async Task ProcessEventsAsync() } try { - subscription.Handler(sessionEvent); + subscription.Invoke(sessionEvent); } catch (Exception ex) { @@ -605,14 +689,162 @@ private async Task ProcessEventsAsync() /// internal void RegisterTools(ICollection tools) { - _toolHandlers.Clear(); + SetToolHandlers(BuildToolHandlerMap(tools)); + } + + private void SetToolHandlers(Dictionary handlers) + { + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + Volatile.Write(ref _toolHandlers, handlers); + } + } + + private static Dictionary BuildToolHandlerMap(ICollection tools) + { + var handlers = new Dictionary(StringComparer.Ordinal); foreach (var tool in tools) { if (tool.GetService() is { } function) { - _toolHandlers.Add(tool.Name, function); + handlers.Add(tool.Name, function); } } + + return handlers; + } + + private static ProtocolExternalToolDefinition ToProtocolExternalToolDefinition(AIFunctionDeclaration function) + { + // Start from the create/resume definition so every path sends the same tool fields. + var definition = CopilotClient.ToolDefinition.FromAIFunction(function); + return new ProtocolExternalToolDefinition + { + Name = definition.Name, + // session.tools.set requires a description string. + Description = definition.Description ?? string.Empty, + Parameters = ToJsonElementDictionary(definition.Parameters), + OverridesBuiltInTool = definition.OverridesBuiltInTool, + SkipPermission = definition.SkipPermission, + Defer = definition.Defer switch + { + CopilotToolDefer.Auto => ProtocolExternalToolDefer.Auto, + CopilotToolDefer.Never => ProtocolExternalToolDefer.Never, + _ => (ProtocolExternalToolDefer?)null, + }, + Metadata = definition.Metadata is { } metadata ? ToJsonElementDictionary(metadata) : null, + IsTerminal = definition.IsTerminal, + }; + } + + private static Dictionary? ToJsonElementDictionary(JsonElement element) + { + if (element.ValueKind != JsonValueKind.Object) + { + return null; + } + + var properties = new Dictionary(StringComparer.Ordinal); + foreach (var property in element.EnumerateObject()) + { + properties.Add(property.Name, property.Value.Clone()); + } + + return properties; + } + + private static Dictionary ToJsonElementDictionary(IDictionary nodes) + { + var properties = new Dictionary(StringComparer.Ordinal); + foreach (var (name, value) in nodes) + { + using var document = JsonDocument.Parse(value?.ToJsonString() ?? "null"); + properties.Add(name, document.RootElement.Clone()); + } + + return properties; + } + + /// + /// Replaces the complete custom tool list supplied by this SDK client for the live session. + /// + /// + /// The same tool declarations accepted by when creating or resuming a session. + /// Passing an empty collection removes all externally implemented tools supplied by this client. + /// + /// + /// A token that cancels the call. Cancelling while an earlier replacement is still in flight sends nothing. Once the + /// request is sent, cancelling only stops the wait: an accepted replacement still installs its handlers. + /// + /// A task that completes after the runtime accepts the replacement. + /// + /// + /// This is a complete replacement for this client's externally implemented tools only. Built-in tools, MCP/plugin tools, + /// extension-discovered tools, subagent tools, and tools supplied by other connections are unchanged. The wire definitions + /// are the same as the tools supplied when creating or resuming a session. + /// + /// + /// Tool handlers switch when the runtime's acceptance response arrives, before subsequent tool requests are dispatched. + /// Tool calls already running finish with the handlers that started them. If the runtime rejects the replacement, the + /// previous handlers remain installed and the exception is propagated. Concurrent calls are applied in order. + /// Acceptance after local retirement fails the replacement without restoring its handlers. + /// + /// + /// The agent sees the new tools from its next model request, which can fall within a turn in progress. A model request + /// already in flight was made with the previous tools, so the agent can still call a tool you removed. This session doesn't + /// answer that call, and it can stay pending until the turn is aborted. If a running turn might still call a tool you remove, + /// replace tools while the session is idle. + /// + /// + [Experimental(Diagnostics.Experimental)] + public async Task SetToolsAsync(ICollection tools, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(tools); + ThrowIfDisposed(); + cancellationToken.ThrowIfCancellationRequested(); + + var wireTools = tools.Select(ToProtocolExternalToolDefinition).ToList(); + var handlers = BuildToolHandlerMap(tools); + + // Cancelling before sending the request leaves handlers unchanged. Once sent, the request runs + // to completion even if the caller stops waiting, so an accepted replacement still installs its handlers. + var replacement = ReplaceToolsAsync(wireTools, handlers, cancellationToken); + try + { + await replacement.WaitAsync(Timeout.InfiniteTimeSpan, cancellationToken); + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + _ = replacement.ContinueWith( + static completed => _ = completed.Exception, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted, + TaskScheduler.Default); + throw; + } + } + + private async Task ReplaceToolsAsync( + List wireTools, + Dictionary handlers, + CancellationToken lockCancellationToken) + { + await _setToolsLock.WaitAsync(lockCancellationToken); + try + { + // SemaphoreSlim can grant a released slot while its cancellation continuation is pending. + lockCancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var request = new ToolsSetRequest { SessionId = SessionId, Tools = wireTools }; + await CopilotClient.InvokeRpcAsync( + JsonRpc, "session.tools.set", [request], null, CancellationToken.None, + onResponseInline: _ => SetToolHandlers(handlers)); + } + finally + { + _setToolsLock.Release(); + } } /// @@ -622,7 +854,12 @@ internal void RegisterTools(ICollection tools) /// The tool if found; otherwise, null. internal AIFunction? GetTool(string name) { - return _toolHandlers.TryGetValue(name, out var tool) ? tool : null; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + var handlers = Volatile.Read(ref _toolHandlers); + return handlers.TryGetValue(name, out var tool) ? tool : null; + } } /// @@ -638,13 +875,21 @@ internal void RegisterPermissionHandler( Func>? handler, bool managedSettingsEnabled) { - _permissionHandler = handler; - _managedSettingsEnabled = managedSettingsEnabled; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + _permissionHandler = handler; + _managedSettingsEnabled = managedSettingsEnabled; + } } internal void RegisterMcpAuthHandler(Func>? handler) { - _mcpAuthHandler = handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + _mcpAuthHandler = handler; + } } /// @@ -654,7 +899,12 @@ internal void RegisterMcpAuthHandler(Func>? /// A task that resolves with the permission decision. internal async Task HandlePermissionRequestAsync(JsonElement permissionRequestData) { - var handler = _permissionHandler; + Func>? handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + handler = _permissionHandler; + } if (handler == null) { @@ -702,7 +952,7 @@ private async Task HandleBroadcastEventAsync(SessionEvent sessionEvent) return; // This client doesn't handle this tool; another client will. using (TelemetryHelpers.RestoreTraceContext(data.Traceparent, data.Tracestate)) - await ExecuteToolAndRespondAsync(data.RequestId, data.ToolName, data.ToolCallId, data.Arguments, tool); + await ExecuteToolAndRespondAsync(data.RequestId, data.ToolName, data.ToolCallId, data.Arguments, tool, toolEvent.AgentId); break; } @@ -719,7 +969,12 @@ private async Task HandleBroadcastEventAsync(SessionEvent sessionEvent) if (data.ResolvedByHook == true) return; // Already resolved by a permissionRequest hook; no client action needed. - var handler = _permissionHandler; + Func>? handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + handler = _permissionHandler; + } if (handler is null) return; // This client doesn't handle permissions; another client will. @@ -733,7 +988,12 @@ private async Task HandleBroadcastEventAsync(SessionEvent sessionEvent) if (string.IsNullOrEmpty(data.RequestId)) return; - var handler = _mcpAuthHandler; + Func>? handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + handler = _mcpAuthHandler; + } if (handler is null) { if (_logger.IsEnabled(LogLevel.Warning)) @@ -917,7 +1177,7 @@ private async Task TryCancelMcpAuthRequestAsync(string requestId) /// /// Executes a tool handler and sends the result back via the HandlePendingToolCall RPC. /// - private async Task ExecuteToolAndRespondAsync(string requestId, string toolName, string toolCallId, JsonElement? arguments, AIFunction tool) + private async Task ExecuteToolAndRespondAsync(string requestId, string toolName, string toolCallId, JsonElement? arguments, AIFunction tool, string? agentId) { if (_externalToolLifetime.IsCancellationRequested) { @@ -942,7 +1202,9 @@ private async Task ExecuteToolAndRespondAsync(string requestId, string toolName, SessionId = SessionId, ToolCallId = toolCallId, ToolName = toolName, - Arguments = arguments + Arguments = arguments, + AgentId = string.IsNullOrEmpty(agentId) ? null : agentId, + RequestId = requestId }; // The built-in tool-search tool receives a snapshot of the session's @@ -1064,6 +1326,17 @@ await Rpc.Tools.HandlePendingToolCallAsync( static string GetSingleParameterName(AIFunction tool) { + if (tool.Name == "apply_patch" && + tool.AdditionalProperties.TryGetValue(CopilotTool.OverridesBuiltInToolKey, out var isOverride) && + isOverride is true && + tool.JsonSchema.TryGetProperty("type", out var schemaType) && + schemaType.ValueKind == JsonValueKind.String && + schemaType.GetString() == "string") + { + // AIFunction uses named arguments even when the override declares a scalar schema. + return "input"; + } + if (tool.JsonSchema.TryGetProperty("properties", out var properties) && properties.ValueKind == JsonValueKind.Object) { @@ -1211,7 +1484,11 @@ private async Task ExecutePermissionAndRespondAsync(string requestId, Permission /// The handler to invoke when user input is requested. internal void RegisterUserInputHandler(Func> handler) { - _userInputHandler = handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + _userInputHandler = handler; + } } /// @@ -1220,11 +1497,18 @@ internal void RegisterUserInputHandler(FuncThe command definitions to register. internal void RegisterCommands(IEnumerable? commands) { - _commandHandlers.Clear(); - if (commands is null) return; - foreach (var cmd in commands) + var handlers = new Dictionary>(StringComparer.Ordinal); + if (commands is not null) + { + foreach (var cmd in commands) + { + handlers[cmd.Name] = cmd.Handler; + } + } + lock (_callbackLock) { - _commandHandlers[cmd.Name] = cmd.Handler; + ThrowIfCallbacksRetired(); + _commandHandlers = handlers; } } @@ -1234,7 +1518,11 @@ internal void RegisterCommands(IEnumerable? commands) /// The handler to invoke when an elicitation request is received. internal void RegisterElicitationHandler(Func>? handler) { - _elicitationHandler = handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + _elicitationHandler = handler; + } } /// @@ -1243,7 +1531,11 @@ internal void RegisterElicitationHandler(FuncThe handler to invoke when an exit-plan-mode request is received. internal void RegisterExitPlanModeHandler(Func>? handler) { - _exitPlanModeHandler = handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + _exitPlanModeHandler = handler; + } } /// @@ -1252,7 +1544,73 @@ internal void RegisterExitPlanModeHandler(FuncThe handler to invoke when an auto-mode-switch request is received. internal void RegisterAutoModeSwitchHandler(Func>? handler) { - _autoModeSwitchHandler = handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + _autoModeSwitchHandler = handler; + } + } + + /// + /// Registers the session-scoped skill provider callback. + /// + internal void RegisterSkillProvider(ISkillProvider? provider) + { + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + _skillProvider = provider; + } + } + + internal void ClearSkillProvider() + { + lock (_callbackLock) + { + _skillProvider = null; + } + } + + internal async ValueTask HandleSkillProviderListAsync(CancellationToken cancellationToken) + { + ISkillProvider provider; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + provider = _skillProvider ?? throw new InvalidOperationException($"No skill provider for session: {SessionId}"); + } + + try + { + var skills = await provider.ListSkillsAsync(cancellationToken).ConfigureAwait(false); + return new CopilotClient.SkillProviderListResult(skills?.ToList() ?? []); + } + catch (Exception ex) when (ex is not OperationCanceledException || !cancellationToken.IsCancellationRequested) + { + LogSkillProviderFailed(ex, "listSkills", SessionId); + throw new InvalidOperationException("Skill provider listSkills failed", ex); + } + } + + internal async ValueTask HandleSkillProviderReadAsync(string name, CancellationToken cancellationToken) + { + ISkillProvider provider; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + provider = _skillProvider ?? throw new InvalidOperationException($"No skill provider for session: {SessionId}"); + } + + try + { + var markdown = await provider.ReadSkillAsync(name, cancellationToken).ConfigureAwait(false); + return new CopilotClient.SkillProviderReadResult(markdown); + } + catch (Exception ex) when (ex is not OperationCanceledException || !cancellationToken.IsCancellationRequested) + { + LogSkillProviderFailed(ex, "readSkill", SessionId); + throw new InvalidOperationException("Skill provider readSkill failed", ex); + } } /// @@ -1269,17 +1627,20 @@ internal void RegisterAutoModeSwitchHandler(FuncMap of provider name to callback, or null/empty to clear. internal void RegisterBearerTokenProviders(IReadOnlyDictionary>>? providers) { - _bearerTokenProviders.Clear(); - if (providers is null || providers.Count == 0) + var callbacks = new Dictionary>>(StringComparer.Ordinal); + if (providers is not null) { - ClientSessionApis.ProviderToken = null; - return; + foreach (var (name, callback) in providers) + { + callbacks[name] = callback; + } } - foreach (var (name, callback) in providers) + lock (_callbackLock) { - _bearerTokenProviders[name] = callback; + ThrowIfCallbacksRetired(); + _bearerTokenProviders = callbacks; + ClientSessionApis.ProviderToken = callbacks.Count == 0 ? null : new BearerTokenProviderHandler(this); } - ClientSessionApis.ProviderToken = new BearerTokenProviderHandler(this); } /// @@ -1290,10 +1651,15 @@ private sealed class BearerTokenProviderHandler(CopilotSession session) : IProvi { public async Task GetTokenAsync(ProviderTokenAcquireRequest request, CancellationToken cancellationToken = default) { - if (!session._bearerTokenProviders.TryGetValue(request.ProviderName, out var callback)) + Func>? callback; + lock (session._callbackLock) { - throw new InvalidOperationException( - $"No bearer-token provider registered for provider \"{request.ProviderName}\""); + session.ThrowIfCallbacksRetired(); + if (!session._bearerTokenProviders.TryGetValue(request.ProviderName, out callback)) + { + throw new InvalidOperationException( + $"No bearer-token provider registered for provider \"{request.ProviderName}\""); + } } var token = await callback(new ProviderTokenArgs { ProviderName = request.ProviderName, SessionId = request.SessionId }).ConfigureAwait(false); return new ProviderTokenAcquireResult { Token = token }; @@ -1376,7 +1742,20 @@ private void RemoveOpenCanvas(string instanceId) internal void SetCanvasHandler(ICanvasHandler? handler) { - ClientSessionApis.Canvas = handler is null ? null : new CanvasHandlerAdapter(handler); + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + ClientSessionApis.Canvas = handler is null ? null : new CanvasHandlerAdapter(handler); + } + } + + internal void SetSessionFsHandler(Rpc.ISessionFsHandler handler) + { + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + ClientSessionApis.SessionFs = handler; + } } private static readonly JsonElement NullJsonElement = JsonElement.Parse("null"); @@ -1445,7 +1824,13 @@ public async Task InvokeAsync(CanvasProviderInvokeActionRequest request, /// private async Task ExecuteCommandAndRespondAsync(string requestId, string commandName, string command, string args) { - if (!_commandHandlers.TryGetValue(commandName, out var handler)) + Func? handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + _commandHandlers.TryGetValue(commandName, out handler); + } + if (handler is null) { try { @@ -1505,7 +1890,12 @@ await handler(new CommandContext /// private async Task HandleElicitationRequestAsync(ElicitationContext context, string requestId) { - var handler = _elicitationHandler; + Func>? handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + handler = _elicitationHandler; + } if (handler is null) return; try @@ -1705,7 +2095,12 @@ public async Task ConfirmAsync(string message, CancellationToken cancellat /// A task that resolves with the user's response. internal async Task HandleUserInputRequestAsync(UserInputRequest request) { - var handler = _userInputHandler ?? throw new InvalidOperationException("No user input handler registered"); + Func> handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + handler = _userInputHandler ?? throw new InvalidOperationException("No user input handler registered"); + } var invocation = new UserInputInvocation { SessionId = SessionId @@ -1727,7 +2122,12 @@ internal async Task HandleUserInputRequestAsync(UserInputRequ /// A task that resolves with the user's decision. internal async Task HandleExitPlanModeRequestAsync(ExitPlanModeRequest request) { - var handler = _exitPlanModeHandler; + Func>? handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + handler = _exitPlanModeHandler; + } if (handler is null) { return new ExitPlanModeResult { Approved = true }; @@ -1750,7 +2150,12 @@ internal async Task HandleExitPlanModeRequestAsync(ExitPlanM /// A task that resolves with the user's decision. internal async Task HandleAutoModeSwitchRequestAsync(AutoModeSwitchRequest request) { - var handler = _autoModeSwitchHandler; + Func>? handler; + lock (_callbackLock) + { + ThrowIfCallbacksRetired(); + handler = _autoModeSwitchHandler; + } if (handler is null) { return AutoModeSwitchResponse.No; @@ -1772,15 +2177,11 @@ internal async Task HandleAutoModeSwitchRequestAsync(Aut /// The hooks configuration. internal void RegisterHooks(SessionHooks hooks) { - _hooksLock.Wait(); - try + lock (_callbackLock) { + ThrowIfCallbacksRetired(); _hooks = hooks; } - finally - { - _hooksLock.Release(); - } } /// @@ -1791,16 +2192,12 @@ internal void RegisterHooks(SessionHooks hooks) /// A task that resolves with the hook output. internal async Task HandleHooksInvokeAsync(string hookType, JsonElement input) { - await _hooksLock.WaitAsync(); SessionHooks? hooks; - try + lock (_callbackLock) { + ThrowIfCallbacksRetired(); hooks = _hooks; } - finally - { - _hooksLock.Release(); - } if (hooks == null) { @@ -1867,6 +2264,16 @@ internal void RegisterHooks(SessionHooks hooks) JsonSerializer.Deserialize(input.GetRawText(), SessionJsonContext.Default.AgentStopHookInput)!, invocation) : null, + "subagentStart" => hooks.OnSubagentStart != null + ? await hooks.OnSubagentStart( + JsonSerializer.Deserialize(input.GetRawText(), SessionJsonContext.Default.SubagentStartHookInput)!, + invocation) + : null, + "subagentStop" => hooks.OnSubagentStop != null + ? await hooks.OnSubagentStop( + JsonSerializer.Deserialize(input.GetRawText(), SessionJsonContext.Default.SubagentStopHookInput)!, + invocation) + : null, _ => null }; } @@ -1894,15 +2301,11 @@ internal void RegisterHooks(SessionHooks hooks) /// The transform callbacks keyed by section identifier. internal void RegisterTransformCallbacks(Dictionary>>? callbacks) { - _transformCallbacksLock.Wait(); - try + lock (_callbackLock) { + ThrowIfCallbacksRetired(); _transformCallbacks = callbacks; } - finally - { - _transformCallbacksLock.Release(); - } } /// @@ -1913,15 +2316,11 @@ internal void RegisterTransformCallbacks(Dictionary HandleSystemMessageTransformAsync(JsonElement sections) { Dictionary>>? callbacks; - await _transformCallbacksLock.WaitAsync(); - try + lock (_callbackLock) { + ThrowIfCallbacksRetired(); callbacks = _transformCallbacks; } - finally - { - _transformCallbacksLock.Release(); - } var parsed = JsonSerializer.Deserialize( sections.GetRawText(), @@ -2067,7 +2466,7 @@ public async Task SetModelAsync(string model, SetModelOptions options, Cancellat if (options.ResetAutoTier) { - var request = new ModelSwitchToRequest + var request = new ModelSwitchToRequestWithSession { SessionId = SessionId, ModelId = model, @@ -2079,7 +2478,7 @@ public async Task SetModelAsync(string model, SetModelOptions options, Cancellat await CopilotClient.InvokeRpcAsync( Rpc, "session.model.switchTo", - [WithExplicitNullAutoTier(request, RpcJsonContext.Default.ModelSwitchToRequest)], + [WithExplicitNullAutoTier(request, RpcJsonContext.Default.ModelSwitchToRequestWithSession)], cancellationToken); return; } @@ -2216,6 +2615,11 @@ public async Task LogAsync(string message, SessionLogLevel? level = null, bool? /// /// After calling this method, the session object can no longer be used. /// + /// + /// Stops subscribed event delivery and releases queued events and subscriptions. + /// Handlers already admitted for dispatch may finish; disposal does not wait for them. + /// Pending response waiters fail if the session closes before their final response. + /// /// /// /// @@ -2235,6 +2639,7 @@ public async ValueTask DisposeAsync() return; } + ClearSkillProvider(); CancelPendingExternalTools(); CloseEventChannel(); @@ -2257,20 +2662,10 @@ public async ValueTask DisposeAsync() } finally { - ReleaseGitHubTokenProviderRegistration(); + Retire(); RemoveFromClient(); GC.SuppressFinalize(this); } - - _eventHandlers = ImmutableInterlocked.InterlockedExchange(ref _eventHandlers, ImmutableArray.Empty); - _toolHandlers.Clear(); - _commandHandlers.Clear(); - - _permissionHandler = null; - _userInputHandler = null; - _elicitationHandler = null; - _exitPlanModeHandler = null; - _autoModeSwitchHandler = null; } [LoggerMessage(Level = LogLevel.Error, Message = "Unhandled exception in broadcast event handler")] @@ -2288,6 +2683,9 @@ public async ValueTask DisposeAsync() [LoggerMessage(Level = LogLevel.Error, Message = "Permission handler or response delivery failed. SessionId={SessionId}, RequestId={RequestId}")] private partial void LogPermissionHandlerOrDeliveryFailed(Exception exception, string sessionId, string requestId); + [LoggerMessage(Level = LogLevel.Warning, Message = "Skill provider {Operation} failed. SessionId={SessionId}")] + private partial void LogSkillProviderFailed(Exception exception, string operation, string sessionId); + internal record SendMessageRequest { public string SessionId { get; init; } = string.Empty; @@ -2373,6 +2771,10 @@ internal void ThrowIfDisposed() [JsonSerializable(typeof(SessionEndHookOutput))] [JsonSerializable(typeof(SessionStartHookInput))] [JsonSerializable(typeof(SessionStartHookOutput))] + [JsonSerializable(typeof(SubagentStartHookInput))] + [JsonSerializable(typeof(SubagentStartHookOutput))] + [JsonSerializable(typeof(SubagentStopHookInput))] + [JsonSerializable(typeof(SubagentStopHookOutput))] [JsonSerializable(typeof(SystemMessageTransformRpcResponse))] [JsonSerializable(typeof(SystemMessageTransformSection))] [JsonSerializable(typeof(Attachment))] diff --git a/dotnet/src/SessionEventJsonConverter.cs b/dotnet/src/SessionEventJsonConverter.cs new file mode 100644 index 0000000000..6b4e9a58a6 --- /dev/null +++ b/dotnet/src/SessionEventJsonConverter.cs @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +using System.Buffers; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization.Metadata; +#if !NETSTANDARD2_0 +using System.Text.Unicode; +#endif + +namespace GitHub.Copilot; + +internal sealed partial class SessionEventJsonConverter +{ + private static readonly Encoding StrictUtf8 = new UTF8Encoding(false, true); + + internal static SessionEvent Deserialize(string json) + { +#if !NETSTANDARD2_0 + const int StackallocThreshold = 512; +#endif + ArgumentNullException.ThrowIfNull(json); + + byte[]? rented = null; + Span buffer = +#if !NETSTANDARD2_0 + json.Length <= StackallocThreshold / 3 ? stackalloc byte[StackallocThreshold] : +#endif + rented = ArrayPool.Shared.Rent(GetUtf8ByteCount(json)); + try + { +#if NETSTANDARD2_0 + int byteCount = StrictUtf8.GetBytes(json, 0, json.Length, rented, 0); +#else + var status = Utf8.FromUtf16(json.AsSpan(), buffer, out _, out int byteCount, replaceInvalidSequences: false); + if (status != OperationStatus.Done) + { + throw new ArgumentException($"Cannot transcode UTF-16 JSON text to UTF-8 ({status}).", nameof(json)); + } +#endif + ReadOnlySpan utf8 = buffer.Slice(0, byteCount); + var reader = new Utf8JsonReader(utf8); + + // Select metadata without entering the converter: reader-based nested deserialization + // would scan the entire object again to scope its input before parsing it. + JsonTypeInfo typeInfo; + try + { + reader.Read(); + typeInfo = ReadEventTypeInfo(ref reader); + } + catch (JsonException ex) when (ex.GetType() != typeof(JsonException)) + { + // Match JsonSerializer's normalization of reader errors. + throw new JsonException(ex.Message, ex.Path, ex.LineNumber, ex.BytePositionInLine, ex); + } + return ToSessionEvent(JsonSerializer.Deserialize(utf8, typeInfo)); + } + finally + { + if (rented is not null) + { + buffer.Clear(); + ArrayPool.Shared.Return(rented); + } + } + } + + private static int GetUtf8ByteCount(string json) + { + try + { + return StrictUtf8.GetByteCount(json); + } + catch (EncoderFallbackException ex) + { + throw new ArgumentException("Cannot transcode invalid UTF-16 JSON text to UTF-8.", nameof(json), ex); + } + } + + private static string ReadString(ref Utf8JsonReader reader) + { + try + { + return reader.GetString()!; + } + catch (InvalidOperationException ex) + { + throw new JsonException(ex.Message, ex); + } + } +} diff --git a/dotnet/src/SessionFsProvider.cs b/dotnet/src/SessionFsProvider.cs index a353c93ad0..9aaf25f4b7 100644 --- a/dotnet/src/SessionFsProvider.cs +++ b/dotnet/src/SessionFsProvider.cs @@ -73,6 +73,22 @@ public interface ISessionFsSqliteProvider Task ExistsAsync(CancellationToken cancellationToken); } +/// Optional interface for providers that support exact binary file reads and writes. +public interface ISessionFsBinaryProvider +{ + /// Reads exact bytes of a file in the session filesystem. + /// Path to read. + /// Cancellation token. + Task ReadFileBytesAsync(string path, CancellationToken cancellationToken); + + /// Writes exact bytes of a file in the session filesystem. + /// Path to write. + /// Exact file bytes. + /// Optional POSIX-style permission mode. + /// Cancellation token. + Task WriteFileBytesAsync(string path, ReadOnlyMemory content, int? mode, CancellationToken cancellationToken); +} + /// /// Optional capability for session filesystem providers that support atomic SQLite transactions. /// @@ -119,73 +135,92 @@ public SessionFsSqliteTransactionException( public SessionFsSqliteTransactionErrorClass ErrorClass { get; } } +/// Thrown from a provider's WriteFileAsync when the failed write changed its target. +public sealed class SessionFsWriteException : Exception +{ + /// Initializes a failed write that changed its target. + /// Human-readable failure description. + /// Optional underlying exception. + public SessionFsWriteException(string message, Exception? innerException = null) + : base(message, innerException) + { + } +} + /// /// Base class for session filesystem providers. Subclasses override the /// virtual methods and use normal C# patterns (return values, throw exceptions). /// The base class catches exceptions and converts them to -/// results expected by the runtime. +/// results expected by the runtime. Existence checks instead return false if the provider throws. /// To add SQLite support, also implement . /// +/// +/// Paths use the configured and are passed to the provider unchanged. +/// Absolute paths refer to the provider's filesystem namespace, not necessarily the runtime host's filesystem. +/// public abstract class SessionFsProvider : ISessionFsHandler { + private const int MaxBinaryBytes = (64 * 1024 * 1024 - 1024) / 4 * 3; + private const int MaxBinaryContentLength = (MaxBinaryBytes + 2) / 3 * 4; + /// Reads the full content of a file. Throw if the file does not exist. - /// SessionFs-relative path. + /// Absolute or relative path in the provider's filesystem namespace. /// Cancellation token. /// The file content as a UTF-8 string. protected abstract Task ReadFileAsync(string path, CancellationToken cancellationToken); - /// Writes content to a file, creating it (and parent directories) if needed. - /// SessionFs-relative path. + /// Writes content to a file, creating it (and parent directories) if needed. Throw if a failed write changed the target. + /// Absolute or relative path in the provider's filesystem namespace. /// Content to write. /// Optional POSIX-style permission mode. Null means use OS default. /// Cancellation token. protected abstract Task WriteFileAsync(string path, string content, int? mode, CancellationToken cancellationToken); /// Appends content to a file, creating it (and parent directories) if needed. - /// SessionFs-relative path. + /// Absolute or relative path in the provider's filesystem namespace. /// Content to append. /// Optional POSIX-style permission mode. Null means use OS default. /// Cancellation token. protected abstract Task AppendFileAsync(string path, string content, int? mode, CancellationToken cancellationToken); /// Checks whether a path exists. - /// SessionFs-relative path. + /// Absolute or relative path in the provider's filesystem namespace. /// Cancellation token. /// true if the path exists, false otherwise. protected abstract Task ExistsAsync(string path, CancellationToken cancellationToken); /// Gets metadata about a file or directory. Throw if the path does not exist. - /// SessionFs-relative path. + /// Absolute or relative path in the provider's filesystem namespace. /// Cancellation token. protected abstract Task StatAsync(string path, CancellationToken cancellationToken); - /// Creates a directory (and optionally parents). Does not fail if it already exists. - /// SessionFs-relative path. + /// Creates a directory. If is true, creates parent directories as needed. + /// Absolute or relative path in the provider's filesystem namespace. /// Whether to create parent directories. /// Optional POSIX-style permission mode (e.g., 0x1FF for 0777). Null means use OS default. /// Cancellation token. protected abstract Task MakeDirectoryAsync(string path, bool recursive, int? mode, CancellationToken cancellationToken); /// Lists entry names in a directory. Throw if the directory does not exist. - /// SessionFs-relative path. + /// Absolute or relative path in the provider's filesystem namespace. /// Cancellation token. protected abstract Task> ReadDirectoryAsync(string path, CancellationToken cancellationToken); /// Lists entries with type info in a directory. Throw if the directory does not exist. - /// SessionFs-relative path. + /// Absolute or relative path in the provider's filesystem namespace. /// Cancellation token. protected abstract Task> ReadDirectoryWithTypesAsync(string path, CancellationToken cancellationToken); /// Removes a file or directory. Throw if the path does not exist (unless is true). - /// SessionFs-relative path. + /// Absolute or relative path in the provider's filesystem namespace. /// Whether to remove directory contents recursively. /// If true, do not throw when the path does not exist. /// Cancellation token. protected abstract Task RemoveAsync(string path, bool recursive, bool force, CancellationToken cancellationToken); /// Renames/moves a file or directory. - /// Source path. - /// Destination path. + /// Absolute or relative source path in the provider's filesystem namespace. + /// Absolute or relative destination path in the provider's filesystem namespace. /// Cancellation token. protected abstract Task RenameAsync(string src, string dest, CancellationToken cancellationToken); @@ -206,6 +241,37 @@ async Task ISessionFsHandler.ReadFileAsync(SessionFsRea } } + async Task ISessionFsHandler.ReadFileBytesAsync(SessionFsReadFileBytesRequest request, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(request); + + try + { + if (this is not ISessionFsBinaryProvider provider) + { + throw new NotSupportedException("Binary reads are not supported by this provider."); + } + var bytes = await provider.ReadFileBytesAsync(request.Path, cancellationToken).ConfigureAwait(false); + if (bytes.Length > MaxBinaryBytes) + { + return new SessionFsReadFileBytesResult + { + Content = string.Empty, + Error = new SessionFsError + { + Code = SessionFsErrorCode.UNKNOWN, + Message = "sessionFs.readFileBytes content exceeds the binary read limit" + } + }; + } + return new SessionFsReadFileBytesResult { Content = Convert.ToBase64String(bytes) }; + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + return new SessionFsReadFileBytesResult { Content = string.Empty, Error = ToSessionFsError(ex) }; + } + } + async Task ISessionFsHandler.WriteFileAsync(SessionFsWriteFileRequest request, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(request); @@ -216,6 +282,39 @@ async Task ISessionFsHandler.ReadFileAsync(SessionFsRea return null; } catch (Exception ex) + { + var error = ToSessionFsError(ex); + if (ex is SessionFsWriteException) + { + error.WriteChanged = true; + } + return error; + } + } + + async Task ISessionFsHandler.WriteFileBytesAsync(SessionFsWriteFileBytesRequest request, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(request); + + try + { + if (this is not ISessionFsBinaryProvider provider) + { + throw new NotSupportedException("Binary writes are not supported by this provider."); + } + if (request.Content.Length > MaxBinaryContentLength) + { + return new SessionFsError { Code = SessionFsErrorCode.UNKNOWN, Message = "sessionFs.writeFileBytes content exceeds the binary write limit" }; + } + var content = Convert.FromBase64String(request.Content); + if (Convert.ToBase64String(content) != request.Content) + { + return new SessionFsError { Code = SessionFsErrorCode.UNKNOWN, Message = "invalid sessionFs.writeFileBytes base64 content" }; + } + await provider.WriteFileBytesAsync(request.Path, content, (int?)request.Mode, cancellationToken).ConfigureAwait(false); + return null; + } + catch (Exception ex) when (ex is not OperationCanceledException) { return ToSessionFsError(ex); } diff --git a/dotnet/src/Types.cs b/dotnet/src/Types.cs index f1f85d1206..c836a095dc 100644 --- a/dotnet/src/Types.cs +++ b/dotnet/src/Types.cs @@ -52,6 +52,74 @@ internal static class Diagnostics internal const string Experimental = "GHCP001"; } +/// +/// Describes a skill supplied by a session-scoped . +/// +/// +/// Experimental. The runtime validates descriptor limits, name syntax, and +/// case-insensitive uniqueness when it reads the provider catalog. +/// +[Experimental(Diagnostics.Experimental)] +public sealed class SkillProviderDescriptor +{ + /// Stable skill name. + [JsonPropertyName("name")] + public required string Name { get; set; } + + /// Human-readable description of what the skill teaches the model. + [JsonPropertyName("description")] + public required string Description { get; set; } + + /// + /// Whether users can invoke the skill explicitly. When unset, the runtime default is . + /// + [JsonPropertyName("userInvocable")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? UserInvocable { get; set; } + + /// + /// Whether model-initiated invocation is disabled. When unset, the runtime default is . + /// + [JsonPropertyName("disableModelInvocation")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? DisableModelInvocation { get; set; } + + /// Optional argument hint shown for explicit invocations. + [JsonPropertyName("argumentHint")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? ArgumentHint { get; set; } +} + +/// +/// Provides session-scoped skills to the Copilot runtime. +/// +/// +/// Experimental. Implementations can be called concurrently and must be safe for +/// concurrent use. The provider is not persisted; supply it again when resuming a +/// session. Returning from +/// reports that the requested skill was not found. +/// +[Experimental(Diagnostics.Experimental)] +public interface ISkillProvider +{ + /// Lists the skills currently available from this provider. + /// + /// Cancels when the runtime abandons the request (for example, on timeout, session disposal, + /// or provider replacement on resume) or the connection closes. + /// + /// The available skill descriptors. + Task> ListSkillsAsync(CancellationToken cancellationToken); + + /// Reads the SKILL.md markdown for a named skill. + /// The skill name requested by the runtime. + /// + /// Cancels when the runtime abandons the request (for example, on timeout, session disposal, + /// or provider replacement on resume) or the connection closes. + /// + /// The skill markdown, or when the skill is not found. + Task ReadSkillAsync(string name, CancellationToken cancellationToken); +} + /// /// Log level for the Copilot runtime. Use the well-known values exposed as /// static members (, , , @@ -890,6 +958,21 @@ public sealed class ToolInvocation /// issuing its own RPC. null for every other tool invocation. /// public IList? AvailableTools { get; set; } + /// + /// Sub-agent instance that issued this call (the + /// of its session events). null for a root-agent call, and for every call + /// when the runtime does not attribute sub-agent tool calls. + /// + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + public string? AgentId { get; set; } + /// + /// Runtime request id of this call: the key session.tools.handlePendingToolCall + /// and session.tools.listPendingRequests use. A root-agent call stays answerable + /// after the session is resumed with pending work continued; a sub-agent's call can + /// be answered only while that sub-agent is still running. + /// + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + public string? RequestId { get; set; } } /// @@ -2052,6 +2135,165 @@ public sealed class AgentStopHookOutput public string? Reason { get; set; } } +/// +/// Input for a subagent-start hook, before the sub-agent's first turn. +/// +public sealed class SubagentStartHookInput +{ + /// + /// The runtime session ID of the parent session. + /// + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// + /// Unix timestamp in milliseconds when the hook was triggered. + /// + [JsonPropertyName("timestamp")] + [JsonConverter(typeof(UnixMillisecondsDateTimeOffsetConverter))] + public DateTimeOffset Timestamp { get; set; } + + /// + /// Current working directory of the parent session. + /// + [JsonPropertyName("cwd")] + public string WorkingDirectory { get; set; } = string.Empty; + + /// + /// Path to the parent session's on-disk transcript, or empty if unavailable. + /// + [JsonPropertyName("transcriptPath")] + public string TranscriptPath { get; set; } = string.Empty; + + /// + /// Name of the sub-agent being started. + /// + [JsonPropertyName("agentName")] + public string AgentName { get; set; } = string.Empty; + + /// + /// Display name of the sub-agent, when available. + /// + [JsonPropertyName("agentDisplayName")] + public string? AgentDisplayName { get; set; } + + /// + /// Description of the sub-agent, when available. + /// + [JsonPropertyName("agentDescription")] + public string? AgentDescription { get; set; } +} + +/// +/// Output for a subagent-start hook. +/// +public sealed class SubagentStartHookOutput +{ + /// + /// Additional context prepended to the sub-agent's initial prompt. + /// + [JsonPropertyName("additionalContext")] + public string? AdditionalContext { get; set; } +} + +/// +/// Input for a subagent-stop hook, after a sub-agent turn completes. +/// +public sealed class SubagentStopHookInput +{ + /// + /// The runtime session ID of the parent session. + /// + [JsonPropertyName("sessionId")] + public string SessionId { get; set; } = string.Empty; + + /// + /// Unix timestamp in milliseconds when the hook was triggered. + /// + [JsonPropertyName("timestamp")] + [JsonConverter(typeof(UnixMillisecondsDateTimeOffsetConverter))] + public DateTimeOffset Timestamp { get; set; } + + /// + /// Current working directory of the parent session. + /// + [JsonPropertyName("cwd")] + public string WorkingDirectory { get; set; } = string.Empty; + + /// + /// Path to the parent session's on-disk transcript, or empty if unavailable. + /// + [JsonPropertyName("transcriptPath")] + public string TranscriptPath { get; set; } = string.Empty; + + /// + /// Name of the sub-agent that stopped. + /// + [JsonPropertyName("agentName")] + public string AgentName { get; set; } = string.Empty; + + /// + /// Display name of the sub-agent, when available. + /// + [JsonPropertyName("agentDisplayName")] + public string? AgentDisplayName { get; set; } + + /// + /// Description of the sub-agent, when available. + /// + [JsonPropertyName("agentDescription")] + public string? AgentDescription { get; set; } + + /// + /// Identifier of the sub-agent, when available. + /// + [JsonPropertyName("agentId")] + public string? AgentId { get; set; } + + /// + /// Type of the sub-agent that stopped. + /// + [JsonPropertyName("agentType")] + public string AgentType { get; set; } = string.Empty; + + /// + /// Reason the sub-agent stopped, such as "end_turn". + /// + [JsonPropertyName("stopReason")] + public string StopReason { get; set; } = string.Empty; + + /// + /// Last assistant response from the sub-agent. + /// + [JsonPropertyName("response")] + public string Response { get; set; } = string.Empty; +} + +/// +/// Output for a subagent-stop hook. +/// +public sealed class SubagentStopHookOutput +{ + /// + /// Set to "block" to request another sub-agent turn, or "allow" to accept it. + /// Other values fail the sub-agent instead of silently allowing its stop. + /// + [JsonPropertyName("decision")] + public string? Decision { get; set; } + + /// + /// Nonempty instruction required when the stop is blocked; invalid without Decision = "block". + /// + [JsonPropertyName("reason")] + public string? Reason { get; set; } + + /// + /// Replacement for the sub-agent's final response when the stop is not blocked. + /// + [JsonPropertyName("modifiedResponse")] + public string? ModifiedResponse { get; set; } +} + /// /// Hook handlers configuration for a session. /// @@ -2108,6 +2350,16 @@ public sealed class SessionHooks /// Handler called when the top-level agent reaches a natural stop. /// public Func>? OnAgentStop { get; set; } + + /// + /// Handler called before a sub-agent's first turn. + /// + public Func>? OnSubagentStart { get; set; } + + /// + /// Handler called after a sub-agent turn completes. + /// + public Func>? OnSubagentStop { get; set; } } /// @@ -2284,9 +2536,9 @@ public sealed class SectionOverride public static SystemMessageSection ToolInstructions { get; } = new("tool_instructions"); /// Repository and organization custom instructions. public static SystemMessageSection CustomInstructions { get; } = new("custom_instructions"); - /// Runtime-provided context and instructions (e.g. system notifications, memories, workspace context, mode-specific instructions, content-exclusion policy). + /// Runtime-provided system-prompt context and instructions, such as system notifications, memories, workspace context, and content-exclusion policy. Mode-specific instructions can travel in transition messages instead. public static SystemMessageSection RuntimeInstructions { get; } = new("runtime_instructions"); - /// End-of-prompt instructions: parallel tool calling, persistence, task completion. + /// End-of-prompt instructions: parallel tool calling, persistence, task completion, and configured subagent-model guidance when the task tool is available. public static SystemMessageSection LastInstructions { get; } = new("last_instructions"); /// Gets the underlying string value of this . @@ -2401,6 +2653,15 @@ public sealed class ProviderConfig [JsonPropertyName("transport")] public string? Transport { get; set; } + /// + /// Product serving the model, such as ollama or lm_studio, reported in telemetry + /// as model_provider. Allowed values are openai, anthropic, + /// azure_openai, ollama, lm_studio, foundry_local, and + /// llama_cpp; only affects telemetry. + /// + [JsonPropertyName("modelProvider")] + public string? ModelProvider { get; set; } + /// /// Base URL of the provider's API endpoint. /// @@ -2571,6 +2832,15 @@ public sealed class NamedProviderConfig [JsonPropertyName("wireApi")] public string? WireApi { get; set; } + /// + /// Product serving this provider's models, such as ollama or lm_studio, reported + /// in telemetry as model_provider. Allowed values are openai, anthropic, + /// azure_openai, ollama, lm_studio, foundry_local, and + /// llama_cpp; only affects telemetry. + /// + [JsonPropertyName("modelProvider")] + public string? ModelProvider { get; set; } + /// /// API endpoint URL. /// @@ -3006,6 +3276,21 @@ public sealed class LargeToolOutputConfig public string? OutputDirectory { get; set; } } +/// Non-persisted opt-in to image generation. +/// Experimental. This API may change or be removed in a future release. +[Experimental(Diagnostics.Experimental)] +public sealed class ImageGenerationConfig +{ + /// + /// Opt in to an authorized Copilot image model. False disables image generation. + /// Omission disables on create/cold resume and preserves resident resume state. + /// Not persisted: re-supply after runtime restart. Policy, permissions, offline + /// mode, and tool filters still apply. BYOK image generation is not supported. + /// + [JsonPropertyName("enabled")] + public bool? Enabled { get; set; } +} + /// /// Overrides the runtime's built-in tool-search behavior. /// Defers tools to keep the model's active tool set small. @@ -3236,6 +3521,14 @@ public sealed class ManagedSettingsPermissions [JsonPropertyName("disableBypassPermissionsMode")] public string? DisableBypassPermissionsMode { get; set; } + /// + /// When , prevents Assisted Permissions from being + /// activated. Omit or set to to impose no restriction. + /// Serialized as disableAssistedPermissionsMode. + /// + [JsonPropertyName("disableAssistedPermissionsMode")] + public bool? DisableAssistedPermissionsMode { get; set; } + /// Tool-permission patterns that are always denied. [JsonPropertyName("deny")] public IList? Deny { get; set; } @@ -3247,6 +3540,15 @@ public sealed class ManagedSettingsPermissions /// Tool-permission patterns that are allowed without prompting. [JsonPropertyName("allow")] public IList? Allow { get; set; } + + /// + /// Closed-world host boundary expressed as Domain(hostname), + /// Domain(IP), or Domain(*.example.com) rules. Schemes, ports, + /// paths, queries, and fragments are rejected. Multiple managed layers + /// intersect their lists. A present empty list denies all hosts. + /// + [JsonPropertyName("limitTo")] + public IList? LimitTo { get; set; } } /// @@ -3343,6 +3645,7 @@ protected SessionConfigBase(SessionConfigBase? other) InfiniteSessions = other.InfiniteSessions; LargeOutput = other.LargeOutput; ToolSearch = other.ToolSearch; + ImageGeneration = other.ImageGeneration; Memory = other.Memory; McpServers = other.McpServers is not null ? (other.McpServers is Dictionary dict @@ -3384,7 +3687,9 @@ protected SessionConfigBase(SessionConfigBase? other) : null; ExpAssignments = other.ExpAssignments; EnableManagedSettings = other.EnableManagedSettings; + EnforceManagedModelDefaults = other.EnforceManagedModelDefaults; ManagedSettings = other.ManagedSettings; + SkillProvider = other.SkillProvider; #pragma warning disable GHCP001 Canvases = other.Canvases is not null ? [.. other.Canvases] : null; RequestCanvasRenderer = other.RequestCanvasRenderer; @@ -3527,6 +3832,20 @@ protected SessionConfigBase(SessionConfigBase? other) /// public bool? EnableSkills { get; set; } + /// + /// Session-scoped skill provider. When set, the runtime can list and read + /// skills by calling back into this SDK host. + /// + /// + /// Experimental. The provider is ephemeral and is never persisted; supply it + /// again when resuming a session. In , + /// set to to make provider + /// skills active. Providers can be called concurrently. + /// + [JsonIgnore] + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + public ISkillProvider? SkillProvider { get; set; } + /// /// Built-in skill names to include in the session. In /// , omitting this option excludes all @@ -3834,6 +4153,10 @@ protected SessionConfigBase(SessionConfigBase? other) /// public ToolSearchConfig? ToolSearch { get; set; } + /// Non-persisted image generation opt-in. Re-supply after runtime restart. + [Experimental(global::GitHub.Copilot.Diagnostics.Experimental)] + public ImageGenerationConfig? ImageGeneration { get; set; } + /// /// Configuration for session memory. When set, controls whether the /// session can read and write persistent memory. @@ -3912,6 +4235,13 @@ protected SessionConfigBase(SessionConfigBase? other) /// public bool? EnableManagedSettings { get; set; } + /// + /// When true, enforces locked managed model controls for this session. + /// Conflicting model changes are rejected; overridable managed defaults + /// remain mutable. + /// + public bool? EnforceManagedModelDefaults { get; set; } + /// /// Optional managed-settings layer injected at session bootstrap. Currently /// carries a permissions object that composes restrictively with any @@ -4690,6 +5020,7 @@ public sealed class SystemMessageTransformRpcResponse [JsonSerializable(typeof(SectionOverride))] [JsonSerializable(typeof(SessionMetadata))] [JsonSerializable(typeof(SetForegroundSessionResponse))] +[JsonSerializable(typeof(SkillProviderDescriptor))] [JsonSerializable(typeof(SystemMessageConfig))] [JsonSerializable(typeof(ToolBinaryResult))] [JsonSerializable(typeof(ToolBinaryResultType))] diff --git a/dotnet/src/build/GitHub.Copilot.SDK.targets b/dotnet/src/build/GitHub.Copilot.SDK.targets index 0c9bfbaf5a..1775fb1159 100644 --- a/dotnet/src/build/GitHub.Copilot.SDK.targets +++ b/dotnet/src/build/GitHub.Copilot.SDK.targets @@ -196,7 +196,7 @@ <_CopilotRuntimeRootAsset Include="$(_CopilotCacheDir)\**\*" - Exclude="$(_CopilotCacheDir)\.copilot-runtime-complete;$(_CopilotCacheDir)\app.js;$(_CopilotCacheDir)\assets\**\*;$(_CopilotCacheDir)\changelog.json;$(_CopilotCacheDir)\copilot;$(_CopilotCacheDir)\copilot.exe;$(_CopilotCacheDir)\copilot.tgz;$(_CopilotCacheDir)\foundry-local-sdk\**\*;$(_CopilotCacheDir)\index.js;$(_CopilotCacheDir)\LICENSE.md;$(_CopilotCacheDir)\napi-oop-runtime\**\*;$(_CopilotCacheDir)\npm-loader.js;$(_CopilotCacheDir)\package.json;$(_CopilotCacheDir)\prebuilds\**\*;$(_CopilotCacheDir)\pvrecorder\**\*;$(_CopilotCacheDir)\queries\**\*;$(_CopilotCacheDir)\README.md;$(_CopilotCacheDir)\sea-loader.js;$(_CopilotCacheDir)\SHA256SUMS.txt;$(_CopilotCacheDir)\tree-sitter*.wasm;$(_CopilotCacheDir)\voice-*.js;$(_CopilotCacheDir)\webview\**\*" /> + Exclude="$(_CopilotCacheDir)\.copilot-runtime-complete;$(_CopilotCacheDir)\app.js;$(_CopilotCacheDir)\assets\**\*;$(_CopilotCacheDir)\changelog.json;$(_CopilotCacheDir)\cli-main.js;$(_CopilotCacheDir)\copilot;$(_CopilotCacheDir)\copilot.exe;$(_CopilotCacheDir)\copilot.tgz;$(_CopilotCacheDir)\foundry-local-sdk\**\*;$(_CopilotCacheDir)\index.js;$(_CopilotCacheDir)\LICENSE.md;$(_CopilotCacheDir)\napi-oop-runtime\**\*;$(_CopilotCacheDir)\npm-loader.js;$(_CopilotCacheDir)\package.json;$(_CopilotCacheDir)\prebuilds\**\*;$(_CopilotCacheDir)\pvrecorder\**\*;$(_CopilotCacheDir)\queries\**\*;$(_CopilotCacheDir)\README.md;$(_CopilotCacheDir)\sea-loader.js;$(_CopilotCacheDir)\SHA256SUMS.txt;$(_CopilotCacheDir)\tree-sitter*.wasm;$(_CopilotCacheDir)\voice-*.js;$(_CopilotCacheDir)\webview\**\*" /> <_CopilotRuntimePrebuildAsset Include="$(_CopilotCacheDir)\prebuilds\$(_CopilotPlatform)\**\*" Exclude="$(_CopilotCacheDir)\prebuilds\$(_CopilotPlatform)\cli-native.node;$(_CopilotCacheDir)\prebuilds\$(_CopilotPlatform)\mediaremote-adapter\**\*;$(_CopilotCacheDir)\prebuilds\$(_CopilotPlatform)\copilot-runtime-bin*" /> @@ -238,7 +238,7 @@ Condition="'$(CopilotCliBinaryPath)' != ''" /> <_CopilotRuntimeRootAsset Include="$(_CopilotCacheDir)\**\*" - Exclude="$(_CopilotCacheDir)\.copilot-runtime-complete;$(_CopilotCacheDir)\app.js;$(_CopilotCacheDir)\assets\**\*;$(_CopilotCacheDir)\changelog.json;$(_CopilotCacheDir)\copilot;$(_CopilotCacheDir)\copilot.exe;$(_CopilotCacheDir)\copilot.tgz;$(_CopilotCacheDir)\foundry-local-sdk\**\*;$(_CopilotCacheDir)\index.js;$(_CopilotCacheDir)\LICENSE.md;$(_CopilotCacheDir)\napi-oop-runtime\**\*;$(_CopilotCacheDir)\npm-loader.js;$(_CopilotCacheDir)\package.json;$(_CopilotCacheDir)\prebuilds\**\*;$(_CopilotCacheDir)\pvrecorder\**\*;$(_CopilotCacheDir)\queries\**\*;$(_CopilotCacheDir)\README.md;$(_CopilotCacheDir)\sea-loader.js;$(_CopilotCacheDir)\SHA256SUMS.txt;$(_CopilotCacheDir)\tree-sitter*.wasm;$(_CopilotCacheDir)\voice-*.js;$(_CopilotCacheDir)\webview\**\*" + Exclude="$(_CopilotCacheDir)\.copilot-runtime-complete;$(_CopilotCacheDir)\app.js;$(_CopilotCacheDir)\assets\**\*;$(_CopilotCacheDir)\changelog.json;$(_CopilotCacheDir)\cli-main.js;$(_CopilotCacheDir)\copilot;$(_CopilotCacheDir)\copilot.exe;$(_CopilotCacheDir)\copilot.tgz;$(_CopilotCacheDir)\foundry-local-sdk\**\*;$(_CopilotCacheDir)\index.js;$(_CopilotCacheDir)\LICENSE.md;$(_CopilotCacheDir)\napi-oop-runtime\**\*;$(_CopilotCacheDir)\npm-loader.js;$(_CopilotCacheDir)\package.json;$(_CopilotCacheDir)\prebuilds\**\*;$(_CopilotCacheDir)\pvrecorder\**\*;$(_CopilotCacheDir)\queries\**\*;$(_CopilotCacheDir)\README.md;$(_CopilotCacheDir)\sea-loader.js;$(_CopilotCacheDir)\SHA256SUMS.txt;$(_CopilotCacheDir)\tree-sitter*.wasm;$(_CopilotCacheDir)\voice-*.js;$(_CopilotCacheDir)\webview\**\*" Condition="Exists('$(_CopilotRuntimeWrapperPath)') And Exists('$(_CopilotRuntimeNodePath)')" /> <_CopilotRuntimePrebuildAsset Include="$(_CopilotCacheDir)\prebuilds\$(_CopilotPlatform)\**\*" Exclude="$(_CopilotCacheDir)\prebuilds\$(_CopilotPlatform)\cli-native.node;$(_CopilotCacheDir)\prebuilds\$(_CopilotPlatform)\mediaremote-adapter\**\*;$(_CopilotCacheDir)\prebuilds\$(_CopilotPlatform)\copilot-runtime-bin*" diff --git a/dotnet/test/E2E/CompactionE2ETests.cs b/dotnet/test/E2E/CompactionE2ETests.cs index 63d4675358..3b85b64a57 100644 --- a/dotnet/test/E2E/CompactionE2ETests.cs +++ b/dotnet/test/E2E/CompactionE2ETests.cs @@ -52,7 +52,20 @@ await session.SendAndWaitAsync(new MessageOptions Assert.True(startEvent.Data.ConversationTokens.GetValueOrDefault() > 0, "Expected compaction to report conversation tokens at start"); Assert.True(completeEvent.Data.Success, "Expected compaction to succeed"); Assert.NotNull(completeEvent.Data.CompactionTokensUsed); - Assert.True(completeEvent.Data.CompactionTokensUsed!.InputTokens.GetValueOrDefault() > 0, "Expected compaction call to consume input tokens"); + // Replay has no prompt usage; recording must preserve the provider's value or absence. + if (Environment.GetEnvironmentVariable("GITHUB_ACTIONS") == "true" + || E2ETestBackendConfiguration.Current != E2ETestBackend.Capi) + { + Assert.Equal(0, completeEvent.Data.CompactionTokensUsed!.InputTokens.GetValueOrDefault()); + } + else + { + var exchanges = await Ctx.GetExchangesAsync(); + var compaction = Assert.Single(exchanges, exchange => + exchange.CompactionUsage is not null + && exchange.CompactionUsage.Summary == completeEvent.Data.SummaryContent); + Assert.Equal(compaction.CompactionUsage!.InputTokens, completeEvent.Data.CompactionTokensUsed!.InputTokens); + } Assert.Contains("", completeEvent.Data.SummaryContent ?? string.Empty, StringComparison.OrdinalIgnoreCase); Assert.Contains("", completeEvent.Data.SummaryContent ?? string.Empty, StringComparison.OrdinalIgnoreCase); Assert.Contains("", completeEvent.Data.SummaryContent ?? string.Empty, StringComparison.OrdinalIgnoreCase); diff --git a/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs b/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs index 18ad960967..39fc5cf3af 100644 --- a/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs +++ b/dotnet/test/E2E/HookLifecycleAndOutputE2ETests.cs @@ -10,13 +10,14 @@ namespace GitHub.Copilot.Test.E2E; /// -/// E2E coverage for every handler exposed on : +/// E2E coverage for session and tool handlers exposed on : /// OnPreToolUse, OnPostToolUse, OnPostToolUseFailure, OnUserPromptSubmitted, /// OnUserPromptTransformed, OnSessionStart, OnSessionEnd, OnErrorOccurred, /// OnAgentStop. Output-shape behavior (modifiedPrompt / modifiedTransformedPrompt / /// additionalContext / errorHandling / modifiedArgs / /// modifiedResult / sessionSummary) is asserted alongside hook invocation. If a -/// new handler is added to SessionHooks, add a corresponding test here. +/// new session or tool handler is added to SessionHooks, add a corresponding +/// test here. Sub-agent lifecycle hooks are covered by . /// public class HookLifecycleAndOutputE2ETests(E2ETestFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, "hooks_extended", output) diff --git a/dotnet/test/E2E/PendingWorkResumeE2ETests.cs b/dotnet/test/E2E/PendingWorkResumeE2ETests.cs index cdbfe82ae8..b2e3a19d26 100644 --- a/dotnet/test/E2E/PendingWorkResumeE2ETests.cs +++ b/dotnet/test/E2E/PendingWorkResumeE2ETests.cs @@ -21,6 +21,7 @@ public class PendingWorkResumeE2ETests(E2ETestFixture fixture, ITestOutputHelper [Fact] public async Task Should_Continue_Pending_Permission_Request_After_Resume() + => await WithSdkDiagnosticsAsync(async () => { var originalPermissionRequest = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var releaseOriginalPermission = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -86,10 +87,11 @@ await session1.SendAsync(new MessageOptions [Description("Transforms a value after permission is granted")] static string ResumePermissionTool([Description("Value to transform")] string value) => $"ORIGINAL_SHOULD_NOT_RUN_{value}"; - } + }); [Fact] public async Task Should_Continue_Pending_External_Tool_Request_After_Resume() + => await WithSdkDiagnosticsAsync(async () => { var originalToolStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var releaseOriginalTool = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -154,7 +156,7 @@ async Task BlockingExternalTool( originalToolStarted.TrySetResult(value); return await releaseOriginalTool.Task.WaitAsync(Timeout.InfiniteTimeSpan, cancellationToken); } - } + }); [Fact] public Task Should_Keep_Pending_External_Tool_Handleable_On_Warm_Resume_When_ContinuePendingWork_Is_False() => @@ -174,6 +176,7 @@ private async Task AssertPendingExternalToolHandleableOnResumeAsync( bool disconnectOriginalClient, bool expectedSessionWasActive, bool expectedHandleResult) + => await WithSdkDiagnosticsAsync(async () => { var originalToolStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var releaseOriginalTool = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -307,10 +310,11 @@ async Task BlockingExternalTool( [Description("Looks up a value after resumption")] string ResumedExternalTool([Description("Value to look up")] string value) => throw new InvalidOperationException("Resumed-session handler should not be invoked"); - } + }); [Fact] public async Task Should_Continue_Parallel_Pending_External_Tool_Requests_After_Resume() + => await WithSdkDiagnosticsAsync(async () => { var originalToolAStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var originalToolBStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -441,7 +445,7 @@ async Task BlockingToolB( originalToolBStarted.TrySetResult(value); return await releaseOriginalToolB.Task.WaitAsync(Timeout.InfiniteTimeSpan, cancellationToken); } - } + }); [Fact] public async Task Should_Resume_Successfully_When_No_Pending_Work_Exists() diff --git a/dotnet/test/E2E/PermissionE2ETests.cs b/dotnet/test/E2E/PermissionE2ETests.cs index 3e01f2132b..78e792c2c2 100644 --- a/dotnet/test/E2E/PermissionE2ETests.cs +++ b/dotnet/test/E2E/PermissionE2ETests.cs @@ -625,6 +625,7 @@ private static string DescribeEvent(SessionEvent evt) _ => evt.Type, }; +#pragma warning disable CS0618 // Compatibility coverage still reads the legacy terminal variant. private static bool ToolCompleteContains(ToolExecutionCompleteEvent evt, string expected) => evt.Data.Result?.Content.Contains(expected, StringComparison.OrdinalIgnoreCase) == true || evt.Data.Result?.DetailedContent?.Contains(expected, StringComparison.OrdinalIgnoreCase) == true || @@ -634,4 +635,5 @@ private static bool ToolCompleteContains(ToolExecutionCompleteEvent evt, string ToolExecutionCompleteContentTerminal terminal => terminal.Text.Contains(expected, StringComparison.OrdinalIgnoreCase), _ => false, }) == true; +#pragma warning restore CS0618 } diff --git a/dotnet/test/E2E/RpcShellAndFleetE2ETests.cs b/dotnet/test/E2E/RpcShellAndFleetE2ETests.cs index 728b8aaf7c..fd023bcd11 100644 --- a/dotnet/test/E2E/RpcShellAndFleetE2ETests.cs +++ b/dotnet/test/E2E/RpcShellAndFleetE2ETests.cs @@ -70,7 +70,7 @@ private static string CreateWriteFileCommand(string markerPath, string marker) { if (OperatingSystem.IsWindows()) { - return $"powershell -NoLogo -NoProfile -Command \"Set-Content -LiteralPath '{markerPath}' -Value '{marker}'\""; + return $"echo {marker}>\"{Path.GetFileName(markerPath)}\""; } return $"sh -c \"printf '%s' '{marker}' > '{markerPath}'\""; diff --git a/dotnet/test/E2E/RuntimeHostE2ETests.cs b/dotnet/test/E2E/RuntimeHostE2ETests.cs index 231f29c733..690e925653 100644 --- a/dotnet/test/E2E/RuntimeHostE2ETests.cs +++ b/dotnet/test/E2E/RuntimeHostE2ETests.cs @@ -44,6 +44,8 @@ private sealed class Application(E2ETestContext context, CopilotClient client) public CopilotSession? Session; public int Creates; public int Resumes; + public string? CreateSessionId; + public string? ResumeSessionId; public int ToolCalls; public readonly ConcurrentQueue HookSessions = new(); public readonly ConcurrentQueue Releases = new(); @@ -82,8 +84,12 @@ private string MagicNumber([Description("A seed value")] string seed) Interlocked.Increment(ref Creates); Assert.False(request.CancellationToken.IsCancellationRequested); Assert.Equal(context.WorkDir, request.Config.WorkingDirectory); + CreateSessionId = request.Config.SessionId; + Assert.False(string.IsNullOrEmpty(CreateSessionId)); Configure(request.Config); - return Session = await context.CreateSessionAsync(client, request.Config); + Session = await context.CreateSessionAsync(client, request.Config); + Assert.Equal(CreateSessionId, Session.SessionId); + return Session; }, ResumeSession = async request => { @@ -91,8 +97,11 @@ private string MagicNumber([Description("A seed value")] string seed) Assert.False(request.CancellationToken.IsCancellationRequested); Assert.False(request.Config.ContinuePendingWork); Assert.Equal(context.WorkDir, request.Config.WorkingDirectory); + ResumeSessionId = request.SessionId; Configure(request.Config); - return Session = await context.ResumeSessionAsync(client, request.SessionId, request.Config); + Session = await context.ResumeSessionAsync(client, request.SessionId, request.Config); + Assert.Equal(ResumeSessionId, Session.SessionId); + return Session; }, OnSessionReleased = session => { @@ -166,7 +175,8 @@ public async Task Creates_Or_Publishes_Exact_Application_Session(bool publish) var created = await ahp.RequestAsync(new() { ["op"] = "create", ["clientId"] = clientId, ["workDir"] = Ctx.WorkDir }); sessionId = created.GetProperty("sessionId").GetString()!; Assert.Equal(1, app.Creates); - Assert.Equal(sessionId, app.Session!.SessionId); + Assert.Equal(app.CreateSessionId, app.Session!.SessionId); + Assert.NotEqual(sessionId, app.Session.SessionId); } var response = await ahp.RequestAsync(new() { @@ -176,7 +186,7 @@ public async Task Creates_Or_Publishes_Exact_Application_Session(bool publish) ["prompt"] = ToolPrompt }); Assert.Contains("MAGIC_hello_42", response.GetProperty("text").GetString()); - await AssertApplicationAsync(app, sessionId); + await AssertApplicationAsync(app, app.Session!.SessionId); await Task.WhenAll(host.DisposeAsync().AsTask(), host.DisposeAsync().AsTask()); await ahp.RequestAsync(new() { ["op"] = "stopped", ["clientId"] = clientId, ["url"] = host.Url }); if (publish) @@ -215,6 +225,9 @@ public async Task Resumes_After_Runtime_Restart_And_Composes_Tools() ["clientTools"] = true }); sessionId = created.GetProperty("sessionId").GetString()!; + Assert.Equal(1, first.Creates); + Assert.Equal(first.CreateSessionId, first.Session!.SessionId); + Assert.NotEqual(sessionId, first.Session.SessionId); var answer = await ahp.RequestAsync(new() { ["op"] = "turn", @@ -247,7 +260,8 @@ public async Task Resumes_After_Runtime_Restart_And_Composes_Tools() .Select(turn => turn.GetProperty("message").GetProperty("text").GetString())); Assert.Equal(0, resumed.Creates); Assert.Equal(1, resumed.Resumes); - Assert.Equal(sessionId, resumed.Session!.SessionId); + Assert.Equal(first.Session!.SessionId, resumed.ResumeSessionId); + Assert.Equal(first.Session.SessionId, resumed.Session!.SessionId); Assert.NotSame(first.Session, resumed.Session); var response = await ahp.RequestAsync(new() { @@ -260,7 +274,7 @@ public async Task Resumes_After_Runtime_Restart_And_Composes_Tools() Assert.Contains("MAGIC_hello_42", response.GetProperty("text").GetString()); Assert.Contains("CLIENT_ECHO_ping", response.GetProperty("text").GetString()); Assert.Equal(1, response.GetProperty("clientToolCalls").GetInt32()); - await AssertApplicationAsync(resumed, sessionId); + await AssertApplicationAsync(resumed, first.Session.SessionId); await replacement.DisposeAsync(); await ahp.RequestAsync(new() { ["op"] = "stopped", ["clientId"] = clientId, ["url"] = replacement.Url }); await resumed.Released.Task.WaitAsync(TimeSpan.FromSeconds(10)); diff --git a/dotnet/test/E2E/ScenarioTestingJsExtensionBridgeE2ETests.cs b/dotnet/test/E2E/ScenarioTestingJsExtensionBridgeE2ETests.cs index 89a50403e3..4ca3092e6b 100644 --- a/dotnet/test/E2E/ScenarioTestingJsExtensionBridgeE2ETests.cs +++ b/dotnet/test/E2E/ScenarioTestingJsExtensionBridgeE2ETests.cs @@ -272,7 +272,7 @@ await TestHelper.WaitForConditionAsync( }, timeout: ExtensionTimeout, pollInterval: TimeSpan.FromMilliseconds(100), - timeoutMessageFactory: () => $"Timed out waiting for extension '{extensionId}' (listed: [{lastExtensions}]; launch markers: {TestHelper.ExtensionLaunchMarkers(Ctx.HomeDir, extensionId)}).", + timeoutMessageFactory: () => $"Timed out waiting for extension '{extensionId}' (listed: [{lastExtensions}]; launch diagnostics: {TestHelper.ExtensionLaunchMarkers(Ctx.HomeDir, extensionId)}).", transientExceptionFilter: ex => ex.ToString().Contains("Extensions not available", StringComparison.OrdinalIgnoreCase)); return extension!; @@ -298,7 +298,7 @@ await TestHelper.WaitForConditionAsync( }, timeout: ExtensionTimeout, pollInterval: TimeSpan.FromMilliseconds(100), - timeoutMessageFactory: () => $"Timed out waiting for canvas from extension '{extensionId}' (listed: [{lastExtensions}]; launch markers: {TestHelper.ExtensionLaunchMarkers(Ctx.HomeDir, extensionId)})."); + timeoutMessageFactory: () => $"Timed out waiting for canvas from extension '{extensionId}' (listed: [{lastExtensions}]; launch diagnostics: {TestHelper.ExtensionLaunchMarkers(Ctx.HomeDir, extensionId)})."); return canvas!; } @@ -515,27 +515,32 @@ function record(kind, data = {}) { }); console.error("[sdk-extension-test] joining"); + let initializationStage = "join"; try { session = await joinSession({ workingDirectory, tools: [], canvases: [canvas] }); + console.error("[sdk-extension-test] joined"); + + initializationStage = "trace"; + record("joined", { + sessionId: session.sessionId, + workspacePath: session.workspacePath ?? null, + workingDirectory, + cwd: process.cwd() + }); + initializationStage = "log"; + await session.log("JS_EXTENSION_LOG"); + console.error("[sdk-extension-test] logged"); } catch (error) { const code = typeof error?.code === "number" ? error.code : "none"; - console.error(`[sdk-extension-test] join failed code=${code}`); + console.error(`[sdk-extension-test] ${initializationStage} failed code=${code}`); + const message = String(error?.message ?? error).replace(/\s+/g, " ").slice(0, 400); + process.stderr.write(`=== scenario-init-failed stage=${initializationStage} name=${error?.name ?? "unknown"} code=${error?.code ?? "none"} message=${message} ===\n`); throw error; } - console.error("[sdk-extension-test] joined"); - - record("joined", { - sessionId: session.sessionId, - workspacePath: session.workspacePath ?? null, - workingDirectory, - cwd: process.cwd() - }); - await session.log("JS_EXTENSION_LOG"); - console.error("[sdk-extension-test] logged"); setInterval(() => {}, 60_000).unref?.(); """; diff --git a/dotnet/test/E2E/ScenarioTestingProvidersE2ETests.cs b/dotnet/test/E2E/ScenarioTestingProvidersE2ETests.cs index 13270d3e43..da8f0eccf2 100644 --- a/dotnet/test/E2E/ScenarioTestingProvidersE2ETests.cs +++ b/dotnet/test/E2E/ScenarioTestingProvidersE2ETests.cs @@ -281,7 +281,7 @@ public async Task Should_Ignore_Failing_Unselected_Provider_But_Surface_Selected Prompt = "This selected provider should fail.", })); Assert.Contains("offline", failure.ToString(), StringComparison.OrdinalIgnoreCase); - Assert.Contains(handler.InferenceRequests, request => request.Host == "offline.scenario.invalid"); + Assert.Single(handler.InferenceRequests, request => request.Host == "offline.scenario.invalid"); } private CopilotClient CreateProviderClient(ScenarioProviderRequestHandler handler) => @@ -385,7 +385,8 @@ protected override async Task SendRequestAsync( if (string.Equals(uri.Host, failingHost, StringComparison.Ordinal)) { - return new HttpResponseMessage(HttpStatusCode.BadGateway) + // Provider selection is the contract here, not transient-error retry backoff. + return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent( "{\"error\":{\"message\":\"offline scenario provider\"}}", diff --git a/dotnet/test/E2E/SessionE2ETests.cs b/dotnet/test/E2E/SessionE2ETests.cs index 9855bc8eae..377bb0147d 100644 --- a/dotnet/test/E2E/SessionE2ETests.cs +++ b/dotnet/test/E2E/SessionE2ETests.cs @@ -476,6 +476,7 @@ public async Task Should_Receive_Session_Events() [Fact] public async Task Send_Returns_Immediately_While_Events_Stream_In_Background() + => await WithSdkDiagnosticsAsync(async () => { var session = await CreateSessionAsync(new SessionConfig { @@ -506,7 +507,7 @@ public async Task Send_Returns_Immediately_While_Events_Stream_In_Background() Assert.Contains("done", message?.Data.Content ?? string.Empty); Assert.Contains("session.idle", events); Assert.Contains("assistant.message", events); - } + }); [Fact] public async Task SendAndWait_Blocks_Until_Session_Idle_And_Returns_Final_Assistant_Message() diff --git a/dotnet/test/E2E/SessionFsE2ETests.cs b/dotnet/test/E2E/SessionFsE2ETests.cs index 146e47d808..5751179a48 100644 --- a/dotnet/test/E2E/SessionFsE2ETests.cs +++ b/dotnet/test/E2E/SessionFsE2ETests.cs @@ -49,6 +49,48 @@ public async Task Should_Route_File_Operations_Through_The_Session_Fs_Provider() } } + [Fact] + // BYOK test models do not advertise vision; only the CAPI test model forwards + // the provider image to the model, which this replay capture verifies. + [Trait(E2ETestTraits.Backend, E2ETestTraits.CapiOnly)] + public async Task Should_View_An_Image_That_Exists_Only_In_The_Binary_Session_Fs_Provider() + { + const string imagePath = "/sdk-provider-image.png"; + var imageBytes = Convert.FromBase64String( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=="); + var providerRoot = CreateProviderRoot(); + try + { + await using var client = CreateSessionFsClient(providerRoot, binary: true); + TestSessionFsHandler? provider = null; + var session = await Ctx.CreateSessionAsync(client, new SessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + CreateSessionFsProvider = s => provider = new TestSessionFsHandler(s.SessionId, providerRoot), + }); + var storedPath = GetStoredPath(providerRoot, session.SessionId, imagePath); + Directory.CreateDirectory(Path.GetDirectoryName(storedPath)!); + await File.WriteAllBytesAsync(storedPath, imageBytes); + Assert.False(File.Exists(imagePath)); + + var msg = await session.SendAndWaitAsync(new MessageOptions + { + Prompt = "Use the view tool to view /sdk-provider-image.png, then reply with exactly SDK_PROVIDER_IMAGE_DONE.", + }); + Assert.Contains("SDK_PROVIDER_IMAGE_DONE", msg?.Data.Content ?? string.Empty); + Assert.Equal(imagePath, provider?.LastBinaryReadPath); + var events = await session.GetEventsAsync(); + Assert.Contains(events.OfType(), asset => + asset.Data.MimeType == "image/png" && + asset.Data.Data == Convert.ToBase64String(imageBytes)); + await session.DisposeAsync(); + } + finally + { + await TryDeleteDirectoryAsync(providerRoot); + } + } + [Fact] public async Task Should_Load_Session_Data_From_Fs_Provider_On_Resume() { @@ -280,7 +322,9 @@ public async Task SessionFsProvider_Converts_Exceptions_To_Rpc_Errors() var handler = (ISessionFsHandler)new ThrowingSessionFsProvider(new FileNotFoundException("missing")); AssertFsError((await handler.ReadFileAsync(new SessionFsReadFileRequest { Path = "missing.txt" })).Error); - AssertFsError(await handler.WriteFileAsync(new SessionFsWriteFileRequest { Path = "missing.txt", Content = "content" })); + var rejectedWrite = await handler.WriteFileAsync(new SessionFsWriteFileRequest { Path = "missing.txt", Content = "content" }); + AssertFsError(rejectedWrite); + Assert.Null(rejectedWrite?.WriteChanged); AssertFsError(await handler.AppendFileAsync(new SessionFsAppendFileRequest { Path = "missing.txt", Content = "content" })); var exists = await handler.ExistsAsync(new SessionFsExistsRequest { Path = "missing.txt" }); @@ -301,6 +345,12 @@ public async Task SessionFsProvider_Converts_Exceptions_To_Rpc_Errors() var unknownError = await unknown.WriteFileAsync(new SessionFsWriteFileRequest { Path = "bad.txt", Content = "content" }); Assert.Equal(SessionFsErrorCode.UNKNOWN, unknownError!.Code); + var partial = (ISessionFsHandler)new ThrowingSessionFsProvider(new SessionFsWriteException("disk full")); + var partialError = await partial.WriteFileAsync(new SessionFsWriteFileRequest { Path = "partial.txt", Content = "content" }); + Assert.Equal(SessionFsErrorCode.UNKNOWN, partialError?.Code); + Assert.True(partialError?.WriteChanged == true); + Assert.Null((await partial.ReadFileAsync(new SessionFsReadFileRequest { Path = "partial.txt" })).Error?.WriteChanged); + static void AssertFsError(SessionFsError? error) { Assert.NotNull(error); @@ -459,7 +509,7 @@ await WaitForConditionAsync( } } - private CopilotClient CreateSessionFsClient(string providerRoot, bool useStdio = true, string? tcpConnectionToken = null) + private CopilotClient CreateSessionFsClient(string providerRoot, bool useStdio = true, string? tcpConnectionToken = null, bool binary = false) { RuntimeConnection connection = useStdio ? RuntimeConnection.ForStdio() @@ -469,7 +519,15 @@ private CopilotClient CreateSessionFsClient(string providerRoot, bool useStdio = return Ctx.CreateClient( options: new CopilotClientOptions { - SessionFs = SessionFsConfig, + SessionFs = binary + ? new SessionFsConfig + { + InitialWorkingDirectory = SessionFsConfig.InitialWorkingDirectory, + SessionStatePath = SessionFsConfig.SessionStatePath, + Conventions = SessionFsConfig.Conventions, + Capabilities = new SessionFsSetProviderCapabilities { Binary = true }, + } + : SessionFsConfig, Connection = connection, }); } @@ -626,8 +684,24 @@ Task ISessionFsSqliteProvider.ExistsAsync(CancellationToken cancellationTo Task.FromException(exception); } - private sealed class TestSessionFsHandler(string sessionId, string rootDir) : SessionFsProvider + private sealed class TestSessionFsHandler(string sessionId, string rootDir) + : SessionFsProvider, ISessionFsBinaryProvider { + public string? LastBinaryReadPath { get; private set; } + + public Task ReadFileBytesAsync(string path, CancellationToken cancellationToken) + { + LastBinaryReadPath = path; + return File.ReadAllBytesAsync(ResolvePath(path), cancellationToken); + } + + public async Task WriteFileBytesAsync(string path, ReadOnlyMemory content, int? mode, CancellationToken cancellationToken) + { + var fullPath = ResolvePath(path); + Directory.CreateDirectory(Path.GetDirectoryName(fullPath)!); + await File.WriteAllBytesAsync(fullPath, content.ToArray(), cancellationToken); + } + protected override async Task ReadFileAsync(string path, CancellationToken cancellationToken) { return await File.ReadAllTextAsync(ResolvePath(path), cancellationToken); diff --git a/dotnet/test/E2E/SetToolsE2ETests.cs b/dotnet/test/E2E/SetToolsE2ETests.cs new file mode 100644 index 0000000000..facb04029c --- /dev/null +++ b/dotnet/test/E2E/SetToolsE2ETests.cs @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +#pragma warning disable GHCP001 // Live tool replacement is intentionally experimental. + +using GitHub.Copilot.Test.Harness; +using Microsoft.Extensions.AI; +using System.ComponentModel; +using Xunit; +using Xunit.Abstractions; + +namespace GitHub.Copilot.Test.E2E; + +public class SetToolsE2ETests(E2ETestFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, "set_tools", output) +{ + private const string FruitPrompt = "Use lookup_fruit to find the fruit for code 42."; + private const string FruitAndVegetablePrompt = "Use lookup_fruit to find the fruit for code 42 again, and use lookup_vegetable to find the vegetable for code 7."; + private const string VegetablePrompt = "Use lookup_vegetable to find the vegetable for code 7."; + + [Fact] + public async Task Replaces_Tools_On_A_Created_Session() + { + var originalLookups = new List(); + var replacementLookups = new List(); + var vegetableLookups = new List(); + var session = await CreateSessionAsync(new SessionConfig + { + Tools = [LookupFruit("apple", originalLookups), RetiredLookup()], + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + var first = await TestHelper.SendAndGetFinalAssistantMessageAsync(session, new MessageOptions { Prompt = FruitPrompt }); + Assert.Contains("apple", first?.Data.Content ?? string.Empty); + + await session.SetToolsAsync([LookupFruit("dragonfruit", replacementLookups), LookupVegetable(vegetableLookups)]); + + var second = await TestHelper.SendAndGetFinalAssistantMessageAsync(session, new MessageOptions { Prompt = FruitAndVegetablePrompt }); + Assert.Contains("dragonfruit", second?.Data.Content ?? string.Empty); + Assert.Contains("carrot", second?.Data.Content ?? string.Empty); + Assert.Equal([42], originalLookups); + Assert.Equal([42], replacementLookups); + Assert.Equal([7], vegetableLookups); + + // Model requests after the replacement offer exactly the new tool set. + var exchanges = await Ctx.GetExchangesAsync(); + var replacedFrom = IndexOfPrompt(exchanges, FruitAndVegetablePrompt); + Assert.True(replacedFrom > 0, "Expected model requests before and after the replacement."); + Assert.All(exchanges.Take(replacedFrom), exchange => + { + var tools = GetToolNames(exchange); + Assert.Contains("lookup_fruit", tools); + Assert.Contains("retired_lookup", tools); + Assert.DoesNotContain("lookup_vegetable", tools); + }); + Assert.All(exchanges.Skip(replacedFrom), exchange => + { + var tools = GetToolNames(exchange); + Assert.Contains("lookup_fruit", tools); + Assert.Contains("lookup_vegetable", tools); + Assert.DoesNotContain("retired_lookup", tools); + }); + } + + [Fact] + public async Task Replaces_Tools_On_A_Resumed_Session() + { + var createdLookups = new List(); + var created = await CreateSessionAsync(new SessionConfig + { + Tools = [LookupFruit("apple", createdLookups)], + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + var sessionId = created.SessionId; + var first = await TestHelper.SendAndGetFinalAssistantMessageAsync(created, new MessageOptions { Prompt = FruitPrompt }); + Assert.Contains("apple", first?.Data.Content ?? string.Empty); + Assert.Equal([42], createdLookups); + await created.DisposeAsync(); + + var fruitLookups = new List(); + var vegetableLookups = new List(); + var resumed = await Ctx.ResumeSessionAsync(Client, sessionId, new ResumeSessionConfig + { + Tools = [LookupFruit("apple", fruitLookups)], + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + await resumed.SetToolsAsync([LookupVegetable(vegetableLookups)]); + + var answer = await TestHelper.SendAndGetFinalAssistantMessageAsync(resumed, new MessageOptions { Prompt = VegetablePrompt }); + Assert.Contains("carrot", answer?.Data.Content ?? string.Empty); + Assert.Equal([7], vegetableLookups); + Assert.Empty(fruitLookups); + + var exchanges = await Ctx.GetExchangesAsync(); + var replacedFrom = IndexOfPrompt(exchanges, VegetablePrompt); + Assert.True(replacedFrom > 0, "Expected model requests before and after the replacement."); + Assert.All(exchanges.Skip(replacedFrom), exchange => + { + var tools = GetToolNames(exchange); + Assert.Contains("lookup_vegetable", tools); + Assert.DoesNotContain("lookup_fruit", tools); + }); + } + + [Fact] + public async Task Keeps_The_Previous_Tools_When_A_Replacement_Is_Rejected() + { + var originalLookups = new List(); + var replacementLookups = new List(); + var session = await CreateSessionAsync(new SessionConfig + { + Tools = [LookupFruit("apple", originalLookups)], + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + await Assert.ThrowsAnyAsync(() => session.SetToolsAsync([ + LookupFruit("dragonfruit", replacementLookups), + InvalidTool(), + ])); + + var answer = await TestHelper.SendAndGetFinalAssistantMessageAsync(session, new MessageOptions { Prompt = FruitPrompt }); + Assert.Contains("apple", answer?.Data.Content ?? string.Empty); + Assert.Equal([42], originalLookups); + Assert.Empty(replacementLookups); + } + + /// Returns the index of the first model request that carries as a user message. + private static int IndexOfPrompt(List exchanges, string prompt) + { + return exchanges.FindIndex(exchange => exchange.Request.Messages.Any(message => + message.Role == "user" && (message.Content?.ToString() ?? string.Empty).Contains(prompt, StringComparison.Ordinal))); + } + + private static AIFunction LookupFruit(string fruit, List? calls = null) + { + string Handler([Description("Fruit code")] int code) + { + calls?.Add(code); + return fruit; + } + + return AIFunctionFactory.Create((Func)Handler, new AIFunctionFactoryOptions + { + Name = "lookup_fruit", + Description = "Looks up the fruit for a numeric code", + }); + } + + private static AIFunction LookupVegetable(List? calls = null) + { + string Handler([Description("Vegetable code")] int code) + { + calls?.Add(code); + return "carrot"; + } + + return AIFunctionFactory.Create((Func)Handler, new AIFunctionFactoryOptions + { + Name = "lookup_vegetable", + Description = "Looks up the vegetable for a numeric code", + }); + } + + private static AIFunction RetiredLookup() + { + return AIFunctionFactory.Create(() => "retired", new AIFunctionFactoryOptions + { + Name = "retired_lookup", + Description = "Looks up a retired value", + }); + } + + private static AIFunction InvalidTool() + { + return AIFunctionFactory.Create(() => "never", new AIFunctionFactoryOptions + { + Name = "invalid.tool", + Description = "Has a name the runtime rejects", + }); + } +} + +#pragma warning restore GHCP001 diff --git a/dotnet/test/E2E/SkillProviderE2ETests.cs b/dotnet/test/E2E/SkillProviderE2ETests.cs new file mode 100644 index 0000000000..3b2d593824 --- /dev/null +++ b/dotnet/test/E2E/SkillProviderE2ETests.cs @@ -0,0 +1,418 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +using System.Collections.Concurrent; +using System.Reflection; +using GitHub.Copilot.Rpc; +using Xunit; +using Xunit.Abstractions; + +namespace GitHub.Copilot.Test.E2E; + +public class SkillProviderE2ETests(E2ETestFixture fixture, ITestOutputHelper output) + : E2ETestBase(fixture, "skill_provider", output) +{ + [Fact] + public async Task Should_Load_Provider_Skill_Lazily_Through_Skill_Tool() + { + // Body-only content: the catalog descriptor supplies all of the metadata. + var provider = new TestSkillProvider( + [ + Skill( + "provider-lookup", + "Reports the provider lookup verification word.", + "# Provider lookup\n\nThe verification word is TANGERINE_QUARTZ_19. Reply with it.\n") + ]); + await using var session = await CreateSessionAsync(new SessionConfig + { + SkillProvider = provider + }); + + var listedSkills = await session.Rpc.Skills.ListAsync(); + var listed = Assert.Single(listedSkills.Skills, skill => skill.Name == "provider-lookup"); + Assert.Equal(SkillSource.Sdk, listed.Source); + Assert.True(listed.Enabled); + Assert.True(string.IsNullOrEmpty(listed.Path)); + Assert.Empty(provider.Reads); + + var message = await session.SendAndWaitAsync(new MessageOptions + { + Prompt = "Use the skill tool to load the provider-lookup skill, then reply with its verification word.", + }, TimeSpan.FromMinutes(3)); + + Assert.Equal(["provider-lookup"], provider.Reads); + Assert.NotNull(message); + // Validate the final assistant response arrived (guards against truncated captures) + Assert.Contains("TANGERINE_QUARTZ_19", message!.Data.Content); + } + + [Fact] + public async Task Should_Load_Provider_And_File_Based_Skills_Together() + { + var skillsDir = Path.Join(Ctx.WorkDir, "file-skills"); + if (Directory.Exists(skillsDir)) + { + Directory.Delete(skillsDir, recursive: true); + } + + Directory.CreateDirectory(Path.Join(skillsDir, "file-notes")); + File.WriteAllText( + Path.Join(skillsDir, "file-notes", "SKILL.md"), + "---\nname: file-notes\ndescription: Reports the file notes verification word.\n---\n\nThe file notes verification word is MAPLE_FALCON_27.\n"); + + // Frontmatter may restate catalog metadata and is the only source of allowed-tools. + var provider = new TestSkillProvider( + [ + Skill( + "provider-audit", + "Reports the provider audit verification word.", + "---\nname: provider-audit\nallowed-tools: view\n---\n\nThe provider audit verification word is COBALT_HERON_58.\n") + ]); + await using var session = await CreateSessionAsync(new SessionConfig + { + SkillDirectories = [skillsDir], + SkillProvider = provider + }); + + var listedSkills = await session.Rpc.Skills.ListAsync(); + var fileSkill = Assert.Single(listedSkills.Skills, skill => skill.Name == "file-notes"); + var providerSkill = Assert.Single(listedSkills.Skills, skill => skill.Name == "provider-audit"); + Assert.NotEqual(SkillSource.Sdk, fileSkill.Source); + Assert.False(string.IsNullOrWhiteSpace(fileSkill.Path)); + Assert.Equal(SkillSource.Sdk, providerSkill.Source); + + var message = await session.SendAndWaitAsync(new MessageOptions + { + Prompt = "Use the skill tool to load the file-notes skill and the provider-audit skill, then reply with both verification words.", + }, TimeSpan.FromMinutes(3)); + + Assert.Equal(["provider-audit"], provider.Reads); + Assert.NotNull(message); + Assert.Contains("MAPLE_FALCON_27", message!.Data.Content); + // Validate the final assistant response arrived (guards against truncated captures) + Assert.Contains("COBALT_HERON_58", message.Data.Content); + } + + [Fact] + public async Task Should_Rebind_Skill_Provider_On_Resume() + { + var original = new TestSkillProvider( + [ + Skill( + "rebind-check", + "Reports the rebind verification word.", + "The rebind verification word is AMBER_ALPHA_11.\n") + ]); + var replacement = new TestSkillProvider( + [ + Skill( + "rebind-check", + "Reports the rebind verification word.", + "The rebind verification word is BRONZE_BETA_22.\n") + ]); + var first = await CreateSessionAsync(new SessionConfig + { + SkillProvider = original + }); + var sessionId = first.SessionId; + var ready = await first.SendAndWaitAsync(new MessageOptions + { + Prompt = "Without using any tools or skills, reply with exactly REBIND_READY.", + }, TimeSpan.FromMinutes(3)); + Assert.NotNull(ready); + Assert.Contains("REBIND_READY", ready!.Data.Content); + + await first.DisposeAsync(); + Assert.Empty(original.Reads); + var originalCallsBeforeResume = original.Calls.Count; + + await using var session = await ResumeSessionAsync(sessionId, new ResumeSessionConfig + { + SkillProvider = replacement + }); + + var message = await session.SendAndWaitAsync(new MessageOptions + { + Prompt = "Use the skill tool to load the rebind-check skill, then reply with its verification word.", + }, TimeSpan.FromMinutes(3)); + + Assert.Equal(["rebind-check"], replacement.Reads); + Assert.Equal(originalCallsBeforeResume, original.Calls.Count); + Assert.NotNull(message); + // Validate the final assistant response arrived (guards against truncated captures) + Assert.Contains("BRONZE_BETA_22", message!.Data.Content); + Assert.DoesNotContain("AMBER_ALPHA_11", message.Data.Content); + } + + [Fact] + public async Task Should_Report_Provider_Read_Failure_Without_Leaking_Details() + { + const string secret = "PROVIDER_SECRET_7F3A9C"; + var provider = new TestSkillProvider( + [ + new ProvidedSkill( + new SkillProviderDescriptor + { + Name = "broken-lookup", + Description = "Reports the broken lookup verification word.", + }, + () => throw new InvalidOperationException($"database unavailable: {secret}")) + ]); + var events = new ConcurrentQueue(); + await using var session = await CreateSessionAsync(new SessionConfig + { + SkillProvider = provider, + OnEvent = events.Enqueue + }); + + var message = await session.SendAndWaitAsync(new MessageOptions + { + Prompt = "Use the skill tool to load the broken-lookup skill. If loading fails, reply with exactly LOAD_FAILED.", + }, TimeSpan.FromMinutes(3)); + + Assert.Contains("broken-lookup", provider.Reads); + var failures = events + .OfType() + .Where(evt => !evt.Data.Success) + .ToArray(); + var failure = Assert.Single(failures); + Assert.DoesNotContain(secret, string.Join("\n", events.Select(evt => evt.ToJson())), StringComparison.Ordinal); + Assert.NotNull(failure.Data.Error); + Assert.NotNull(message); + // Validate the final assistant response arrived (guards against truncated captures) + Assert.Contains("LOAD_FAILED", message!.Data.Content); + } + + [Fact] + public async Task Should_Report_Missing_Provider_Skill_As_Not_Found() + { + var provider = new TestSkillProvider( + [ + new ProvidedSkill( + new SkillProviderDescriptor + { + Name = "vanished-lookup", + Description = "Reports the vanished lookup verification word.", + }, + () => null) + ]); + var events = new ConcurrentQueue(); + await using var session = await CreateSessionAsync(new SessionConfig + { + SkillProvider = provider, + OnEvent = events.Enqueue + }); + + var message = await session.SendAndWaitAsync(new MessageOptions + { + Prompt = "Use the skill tool to load the vanished-lookup skill. If loading fails, reply with exactly LOAD_FAILED.", + }, TimeSpan.FromMinutes(3)); + + Assert.Contains("vanished-lookup", provider.Reads); + var failures = events + .OfType() + .Where(evt => !evt.Data.Success) + .ToArray(); + var failure = Assert.Single(failures); + Assert.Contains("not found", failure.ToJson(), StringComparison.OrdinalIgnoreCase); + Assert.NotNull(message); + // Validate the final assistant response arrived (guards against truncated captures) + Assert.Contains("LOAD_FAILED", message!.Data.Content); + } + + [Fact] + public async Task Should_Keep_Provider_Dormant_When_Skills_Disabled() + { + var provider = new TestSkillProvider( + [ + Skill("dormant-lookup", "Never listed.", "Never read.\n") + ]); + await using var session = await CreateSessionAsync(new SessionConfig + { + EnableSkills = false, + SkillProvider = provider + }); + + await session.Rpc.Skills.EnsureLoadedAsync(); + var listedSkills = await session.Rpc.Skills.ListAsync(); + + Assert.DoesNotContain(listedSkills.Skills, skill => skill.Source == SkillSource.Sdk); + Assert.Empty(provider.Calls); + } + + [Fact] + public async Task Should_Unbind_Provider_When_Resumed_Without_One() + { + var provider = new TestSkillProvider( + [ + Skill("unbound-lookup", "Reports the unbound lookup word.", "Unbound.\n") + ]); + var first = await CreateSessionAsync(new SessionConfig + { + SkillProvider = provider + }); + var before = await first.Rpc.Skills.ListAsync(); + Assert.Contains(before.Skills, skill => skill.Name == "unbound-lookup"); + var callsBeforeResume = provider.Calls.Count; + + UntrackSessionWithoutDetach(first); + + CopilotSession? session = null; + try + { + session = await Ctx.ResumeSessionAsync(Client, first.SessionId, new ResumeSessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + await session.Rpc.Skills.ReloadAsync(); + var listedSkills = await session.Rpc.Skills.ListAsync(); + + Assert.DoesNotContain(listedSkills.Skills, skill => skill.Source == SkillSource.Sdk); + Assert.Equal(callsBeforeResume, provider.Calls.Count); + } + finally + { + if (session is not null) + { + await session.DisposeAsync(); + } + + await first.DisposeAsync(); + } + } + + [Fact] + public async Task Should_Cancel_A_Blocked_Provider_Call_When_The_Session_Is_Disposed() + { + var provider = new BlockingSkillProvider(); + var session = await CreateSessionAsync(new SessionConfig + { + SkillProvider = provider + }); + + // The list RPC may fail or omit provider skills once the binding is removed; + // only the provider's cancellation matters here. + var list = session.Rpc.Skills.ListAsync(); + await provider.Entered.WaitAsync(TimeSpan.FromSeconds(30)); + + await session.DisposeAsync(); + + await provider.Cancelled.WaitAsync(TimeSpan.FromSeconds(10)); + await Record.ExceptionAsync(() => list); + } + + [Fact] + public async Task Should_Reject_Skill_Provider_For_Cloud_Sessions() + { + var provider = new TestSkillProvider( + [ + Skill("cloud-lookup", "Never listed.", "Never read.\n") + ]); + + var exception = await Assert.ThrowsAsync(() => + Client.CreateSessionAsync(new SessionConfig + { + Cloud = new CloudSessionOptions(), + SkillProvider = provider, + })); + + Assert.Equal("Skill providers are not supported for cloud sessions.", exception.Message); + Assert.Empty(provider.Calls); + } + + private static ProvidedSkill Skill(string name, string description, string markdown) => + new( + new SkillProviderDescriptor + { + Name = name, + Description = description, + }, + () => markdown); + + private static void UntrackSessionWithoutDetach(CopilotSession session) + { + // Match a warm runtime resume: remove the SDK wrapper so resume is allowed, but do not detach the runtime session. + var removeFromClient = typeof(CopilotSession).GetMethod( + "RemoveFromClient", + BindingFlags.Instance | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("CopilotSession.RemoveFromClient was not found."); + removeFromClient.Invoke(session, null); + } + + private sealed record ProvidedSkill(SkillProviderDescriptor Descriptor, Func Read); + + private sealed class BlockingSkillProvider : ISkillProvider + { + private readonly TaskCompletionSource _entered = new(TaskCreationOptions.RunContinuationsAsynchronously); + private readonly TaskCompletionSource _cancelled = new(TaskCreationOptions.RunContinuationsAsynchronously); + + public Task Entered => _entered.Task; + + public Task Cancelled => _cancelled.Task; + + public async Task> ListSkillsAsync(CancellationToken cancellationToken) + { + _entered.TrySetResult(); + try + { + await Task.Delay(Timeout.Infinite, cancellationToken); + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + // Not a token callback: if cancellation resumes this method inline, disposing + // a registration whose callback has not run yet drops it. + _cancelled.TrySetResult(); + throw; + } + + return []; + } + + public Task ReadSkillAsync(string name, CancellationToken cancellationToken) => + Task.FromResult(null); + } + + private sealed class TestSkillProvider(IReadOnlyList skills) : ISkillProvider + { + private readonly object _lock = new(); + private readonly List _calls = []; + + public IReadOnlyList Calls + { + get + { + lock (_lock) + { + return [.. _calls]; + } + } + } + + public IReadOnlyList Reads => Calls + .Where(call => call.StartsWith("read:", StringComparison.Ordinal)) + .Select(call => call["read:".Length..]) + .ToArray(); + + public Task> ListSkillsAsync(CancellationToken cancellationToken) + { + AddCall("list"); + return Task.FromResult>(skills.Select(skill => skill.Descriptor).ToArray()); + } + + public Task ReadSkillAsync(string name, CancellationToken cancellationToken) + { + AddCall($"read:{name}"); + return Task.FromResult(skills.FirstOrDefault(skill => skill.Descriptor.Name == name)?.Read()); + } + + private void AddCall(string call) + { + lock (_lock) + { + _calls.Add(call); + } + } + } +} diff --git a/dotnet/test/E2E/StructuredOutputE2ETests.cs b/dotnet/test/E2E/StructuredOutputE2ETests.cs index b18e93b492..e82e31d008 100644 --- a/dotnet/test/E2E/StructuredOutputE2ETests.cs +++ b/dotnet/test/E2E/StructuredOutputE2ETests.cs @@ -392,6 +392,15 @@ public async Task Rejects_Unsupported_Or_Oversized_Schemas_Before_Admission() { var config = StructuredSessionConfig(); config.Model = model; + config.EnableExperimentalMode = model == "hydrafusion"; + if (model == "hydrafusion") + { + config.FeatureFlags = new Dictionary + { + ["HYDRAFUSION"] = true, + ["HYDRAFUSION_ROLLOUT"] = true, + }; + } config.OnPermissionRequest = PermissionHandler.ApproveAll; await using var session = await Ctx.CreateSessionAsync(client, config); await Assert.ThrowsAsync(() => session.SendAndWaitAsync( diff --git a/dotnet/test/E2E/SubagentHooksE2ETests.cs b/dotnet/test/E2E/SubagentHooksE2ETests.cs index c5d8c80fe2..fdbb779639 100644 --- a/dotnet/test/E2E/SubagentHooksE2ETests.cs +++ b/dotnet/test/E2E/SubagentHooksE2ETests.cs @@ -4,6 +4,7 @@ using System.Collections.Concurrent; using System.Net.Http; +using System.Text.Json; using GitHub.Copilot.Test.Harness; using Xunit; using Xunit.Abstractions; @@ -15,10 +16,16 @@ namespace GitHub.Copilot.Test.E2E; public class SubagentHooksE2ETests(E2ETestFixture fixture, ITestOutputHelper output) : E2ETestBase(fixture, "subagent_hooks", output) { + private const string ChildContext = "Subagent start hook verified: read the requested file."; + private const string StopResponsePrefix = "Subagent stop hook verified: "; + [Fact] - public async Task Should_Invoke_PreToolUse_And_PostToolUse_Hooks_For_Sub_Agent_Tool_Calls() + public async Task Should_Apply_Subagent_Lifecycle_Hook_Outputs() { var hookLog = new ConcurrentBag<(string Kind, string ToolName, string SessionId)>(); + var lifecycle = new ConcurrentQueue(); + var starts = new ConcurrentQueue<(SubagentStartHookInput Input, string InvocationSessionId)>(); + var stops = new ConcurrentQueue<(SubagentStopHookInput Input, string InvocationSessionId)>(); var requestHandler = new RecordingForwardingRequestHandler(); const string waitingText = "I've launched an explore agent to read subagent-test.txt. Waiting for it to complete..."; const string finalText = "The explore agent successfully read the file. The contents of **subagent-test.txt** are:\n\n```\nHello from subagent test!\n```"; @@ -58,6 +65,24 @@ public async Task Should_Invoke_PreToolUse_And_PostToolUse_Hooks_For_Sub_Agent_T } return null; }, + OnSubagentStart = (input, invocation) => + { + starts.Enqueue((input, invocation.SessionId)); + lifecycle.Enqueue("start"); + return Task.FromResult(new SubagentStartHookOutput + { + AdditionalContext = ChildContext + }); + }, + OnSubagentStop = (input, invocation) => + { + stops.Enqueue((input, invocation.SessionId)); + lifecycle.Enqueue("stop"); + return Task.FromResult(new SubagentStopHookOutput + { + ModifiedResponse = StopResponsePrefix + input.Response + }); + }, }, }); @@ -95,6 +120,34 @@ public async Task Should_Invoke_PreToolUse_And_PostToolUse_Hooks_For_Sub_Agent_T parentWaiting.TrySetResult(true); } + Assert.Collection(lifecycle, + kind => Assert.Equal("start", kind), + kind => Assert.Equal("stop", kind)); + + var start = Assert.Single(starts); + Assert.Equal(session.SessionId, start.InvocationSessionId); + Assert.Equal(session.SessionId, start.Input.SessionId); + Assert.True(start.Input.Timestamp > DateTimeOffset.UnixEpoch); + Assert.Equal(Path.GetFullPath(Ctx.WorkDir), Path.GetFullPath(start.Input.WorkingDirectory)); + Assert.Equal("explore", start.Input.AgentName); + Assert.Null(start.Input.AgentDisplayName); + Assert.Null(start.Input.AgentDescription); + + var stop = Assert.Single(stops); + Assert.Equal(session.SessionId, stop.InvocationSessionId); + Assert.Equal(session.SessionId, stop.Input.SessionId); + Assert.True(stop.Input.Timestamp > DateTimeOffset.UnixEpoch); + Assert.True(stop.Input.Timestamp >= start.Input.Timestamp); + Assert.Equal(Path.GetFullPath(Ctx.WorkDir), Path.GetFullPath(stop.Input.WorkingDirectory)); + Assert.Equal(start.Input.TranscriptPath, stop.Input.TranscriptPath); + Assert.Equal(start.Input.AgentName, stop.Input.AgentName); + Assert.Equal(start.Input.AgentDisplayName, stop.Input.AgentDisplayName); + Assert.Equal(start.Input.AgentDescription, stop.Input.AgentDescription); + Assert.False(string.IsNullOrEmpty(stop.Input.AgentId)); + Assert.Equal("explore", stop.Input.AgentType); + Assert.Equal("end_turn", stop.Input.StopReason); + Assert.Contains("Hello from subagent test!", stop.Input.Response); + var log = hookLog.ToArray(); // Parent tool hooks fire for "task" @@ -110,6 +163,30 @@ public async Task Should_Invoke_PreToolUse_And_PostToolUse_Hooks_For_Sub_Agent_T // input.SessionId distinguishes parent from sub-agent Assert.NotEqual(viewPre[0].SessionId, taskPre[0].SessionId); AssertSubagentRequestMetadata(requestHandler.InferenceRequests); + Assert.Contains(requestHandler.InferenceRequests, r => + { + if (string.IsNullOrEmpty(r.ParentAgentId)) return false; + using var body = JsonDocument.Parse(r.Body); + if (!body.RootElement.TryGetProperty("messages", out var messages) + && !body.RootElement.TryGetProperty("input", out messages)) + return false; + return messages.ValueKind == JsonValueKind.Array && messages.EnumerateArray().Any(message => + message.ValueKind == JsonValueKind.Object + && message.TryGetProperty("role", out var role) + && role.ValueKind == JsonValueKind.String + && role.GetString() == "user" + && message.TryGetProperty("content", out var content) + && (content.ValueKind == JsonValueKind.String + ? content.GetString()!.Contains(ChildContext + "\n\nRead the file \"subagent-test.txt\"", StringComparison.Ordinal) + : content.ValueKind == JsonValueKind.Array && content.EnumerateArray().Any(part => + part.ValueKind == JsonValueKind.Object + && part.TryGetProperty("text", out var text) + && text.ValueKind == JsonValueKind.String + && text.GetString()!.Contains(ChildContext + "\n\nRead the file \"subagent-test.txt\"", StringComparison.Ordinal)))); + }); + Assert.Contains(requestHandler.InferenceRequests, r => + string.IsNullOrEmpty(r.ParentAgentId) + && r.Body.Contains(StopResponsePrefix, StringComparison.Ordinal)); } private static void AssertSubagentRequestMetadata(IReadOnlyCollection records) @@ -131,14 +208,18 @@ private sealed class RecordingForwardingRequestHandler : CopilotRequestHandler public IReadOnlyCollection InferenceRequests => [.. _records.Where(r => RecordingRequestHandler.IsInferenceUrl(r.Url))]; - protected override Task SendRequestAsync(HttpRequestMessage request, CopilotRequestContext ctx) + protected override async Task SendRequestAsync(HttpRequestMessage request, CopilotRequestContext ctx) { + var body = RecordingRequestHandler.IsInferenceUrl(request.RequestUri!.ToString()) + ? await request.Content!.ReadAsStringAsync() + : string.Empty; _records.Add(new RequestRecord( request.RequestUri!.ToString(), ctx.AgentId, ctx.ParentAgentId, - ctx.InteractionType)); - return base.SendRequestAsync(request, ctx); + ctx.InteractionType, + body)); + return await base.SendRequestAsync(request, ctx); } } @@ -146,5 +227,6 @@ private sealed record RequestRecord( string Url, string? AgentId, string? ParentAgentId, - string? InteractionType); + string? InteractionType, + string Body); } diff --git a/dotnet/test/E2E/ToolsE2ETests.cs b/dotnet/test/E2E/ToolsE2ETests.cs index 943e49f45f..e5714848ad 100644 --- a/dotnet/test/E2E/ToolsE2ETests.cs +++ b/dotnet/test/E2E/ToolsE2ETests.cs @@ -319,6 +319,67 @@ string CaptureInput(object input) } } + [Theory] + [InlineData(true)] + [InlineData(false)] + [Trait(E2ETestTraits.Backend, E2ETestTraits.SelfConfiguredBackend)] + public async Task ApplyPatch_StringSchemaOverride_Binds_Patch_Input(bool useCustomToolCall) + { + var receivedInput = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + JsonElement? receivedArguments = null; + var invocationCount = 0; + var handler = new ApplyPatchOverrideRequestHandler(useCustomToolCall); + await using var client = Ctx.CreateClient(options: new CopilotClientOptions + { + Connection = RuntimeConnection.ForStdio(), + RequestHandler = handler, + }); + await client.StartAsync(); + + var inner = CopilotTool.DefineTool( + (string input, ToolInvocation invocation) => + { + receivedArguments = invocation.Arguments; + Interlocked.Increment(ref invocationCount); + receivedInput.TrySetResult(input); + return ApplyPatchResult; + }, + new CopilotToolOptions { OverridesBuiltInTool = true, SkipPermission = true }, + new AIFunctionFactoryOptions { Name = "apply_patch", Description = "Host-implemented apply_patch" }); + var tool = new StringSchemaApplyPatchTool(inner); + Assert.Equal("string", tool.JsonSchema.GetProperty("type").GetString()); + + await using var session = await Ctx.CreateSessionAsync(client, new SessionConfig + { + Model = "gpt-4o-mini", + Provider = new ProviderConfig + { + Type = "openai", + WireApi = "completions", + BaseUrl = "https://apply-patch.invalid/v1", + ApiKey = "test-key", + ModelId = "gpt-4o-mini", + WireModel = "gpt-4o-mini", + }, + Streaming = true, + Tools = [tool], + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + var message = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Use apply_patch" }); + + Assert.True(receivedInput.Task.IsCompleted); + Assert.Equal(ApplyPatchInput, await receivedInput.Task); + Assert.Equal(JsonValueKind.String, receivedArguments?.ValueKind); + Assert.Equal(ApplyPatchInput, receivedArguments?.GetString()); + Assert.Equal(1, Volatile.Read(ref invocationCount)); + Assert.Equal("override complete", message?.Data.Content); + var requests = handler.InferenceRequests; + Assert.Equal(2, requests.Count); + AssertApplyPatchOverrideAdvertised(requests[0], "string"); + AssertApplyPatchResultReachedModel(requests[1]); + } + [Fact] public async Task SkipPermission_Sent_In_Tool_Definition() { @@ -573,6 +634,17 @@ private static void AssertApplyPatchResultReachedModel(string requestBody) Assert.Equal(ApplyPatchResult, toolResult.GetProperty("content").GetString()); } + private sealed class StringSchemaApplyPatchTool(AIFunction inner) : DelegatingAIFunction(inner) + { + public override JsonElement JsonSchema { get; } = CreateSchema(); + + private static JsonElement CreateSchema() + { + using var document = JsonDocument.Parse("""{"type":"string"}"""); + return document.RootElement.Clone(); + } + } + private sealed class ApplyPatchOverrideRequestHandler(bool useCustomToolCall) : CopilotRequestHandler { private const string CustomToolCallResponse = diff --git a/dotnet/test/GitHub.Copilot.SDK.Test.csproj b/dotnet/test/GitHub.Copilot.SDK.Test.csproj index 3f1c24f973..9c8b6fc9c5 100644 --- a/dotnet/test/GitHub.Copilot.SDK.Test.csproj +++ b/dotnet/test/GitHub.Copilot.SDK.Test.csproj @@ -16,6 +16,12 @@ false + + + $(RuntimeIdentifier) + $(NETCoreSdkPortableRuntimeIdentifier) + + diff --git a/dotnet/test/Harness/E2ETestBase.cs b/dotnet/test/Harness/E2ETestBase.cs index a9b18bf6cf..679fbb9c6b 100644 --- a/dotnet/test/Harness/E2ETestBase.cs +++ b/dotnet/test/Harness/E2ETestBase.cs @@ -6,6 +6,7 @@ using GitHub.Copilot.Test.Harness; using Microsoft.Extensions.Logging; using System.Data; +using System.Diagnostics; using System.Reflection; using Xunit; using Xunit.Abstractions; @@ -18,6 +19,7 @@ public abstract class E2ETestBase : IClassFixture, IAsyncLifetim private readonly string _snapshotCategory; private readonly string _testName; private readonly bool _replayOnly; + private readonly ITestOutputHelper _output; protected E2ETestContext Ctx => _fixture.Ctx; protected CopilotClient Client => _fixture.Client; @@ -32,10 +34,12 @@ protected E2ETestBase( _snapshotCategory = snapshotCategory; _testName = GetTestName(output); _replayOnly = replayOnly; + _output = output; Logger = new XunitLogger(output); // 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"; } /// Logger that forwards warnings and above to xunit test output. @@ -75,6 +79,53 @@ public Task DisposeAsync() return Ctx.CleanupAfterTestAsync(); } + protected async Task WithSdkDiagnosticsAsync(Func test) + { + try + { + await test(); + } + catch + { + using var process = new Process + { + StartInfo = new ProcessStartInfo("node") + { + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }, + }; + var started = false; + try + { + process.StartInfo.Arguments = string.Join(" ", + new[] { Path.Join(FindTestHarnessDir(), "sdk-diagnostics.mjs"), Ctx.HomeDir } + .Select(argument => "\"" + argument.Replace("\"", "\\\"") + "\"")); + process.Start(); + started = true; + var text = process.StandardOutput.ReadToEndAsync(); + var errors = process.StandardError.ReadToEndAsync(); + await Task.WhenAll(text, errors, process.WaitForExitAsync()).WaitAsync(TimeSpan.FromSeconds(5)); + _output.WriteLine(process.ExitCode == 0 ? await text : "SDK diagnostics unavailable"); + } + catch + { + try + { + if (started && !process.HasExited) process.Kill(entireProcessTree: true); + } + catch (Exception error) when (error is InvalidOperationException or System.ComponentModel.Win32Exception) + { + _output.WriteLine("SDK diagnostics collector cleanup failed"); + } + _output.WriteLine("SDK diagnostics unavailable"); + } + throw; + } + } + /// /// Creates a session with a default config that approves all permissions. /// Convenience wrapper for E2E tests. diff --git a/dotnet/test/Harness/E2ETestContext.cs b/dotnet/test/Harness/E2ETestContext.cs index b399372717..a5b76fbcd2 100644 --- a/dotnet/test/Harness/E2ETestContext.cs +++ b/dotnet/test/Harness/E2ETestContext.cs @@ -25,6 +25,7 @@ public sealed class E2ETestContext : IAsyncDisposable /// Optional logger injected by tests; applied to all clients created via . public ILogger? Logger { get; set; } + public bool DiagnosticsEnabled { get; set; } private readonly ReplayProxy _proxy; private readonly string _repoRoot; @@ -302,6 +303,10 @@ public Dictionary GetEnvironment() // disables the method (runtime filters out empty HMAC keys). env["COPILOT_HMAC_KEY"] = ""; env["CAPI_HMAC_KEY"] = ""; + if (DiagnosticsEnabled) + { + env["RUST_LOG"] = "error,sdk_diagnostics=debug"; + } return env!; } @@ -322,6 +327,10 @@ public CopilotClient CreateClient( options ??= new CopilotClientOptions(); options.Logger ??= Logger; + if (DiagnosticsEnabled) + { + options.LogLevel = CopilotLogLevel.Debug; + } // Resolve the working directory the worker should run in. Child-process and // URI transports take it as a per-client option; the in-process transport diff --git a/dotnet/test/Harness/E2ETestFixture.cs b/dotnet/test/Harness/E2ETestFixture.cs index 9bc0efcf71..b66476670f 100644 --- a/dotnet/test/Harness/E2ETestFixture.cs +++ b/dotnet/test/Harness/E2ETestFixture.cs @@ -12,15 +12,15 @@ public class E2ETestFixture : IAsyncLifetime internal const string SharedTcpConnectionToken = "e2e-shared-token"; public E2ETestContext Ctx { get; private set; } = null!; - public CopilotClient Client { get; private set; } = null!; + private CopilotClient? _client; + public CopilotClient Client => _client ??= Ctx.CreateClient(options: new CopilotClientOptions + { + Connection = CreateSharedConnection(E2ETestContext.UsesInProcessTransport), + }, persistent: true); public async Task InitializeAsync() { Ctx = await E2ETestContext.CreateAsync(); - Client = Ctx.CreateClient(options: new CopilotClientOptions - { - Connection = CreateSharedConnection(E2ETestContext.UsesInProcessTransport), - }, persistent: true); } internal static RuntimeConnection CreateSharedConnection(bool useInProcessTransport) => diff --git a/dotnet/test/Harness/ReplayProxy.cs b/dotnet/test/Harness/ReplayProxy.cs index 0f9bd1dc4c..140642a2f7 100644 --- a/dotnet/test/Harness/ReplayProxy.cs +++ b/dotnet/test/Harness/ReplayProxy.cs @@ -40,18 +40,14 @@ async Task StartCoreAsync() }; var process = new Process { StartInfo = startInfo }; - - var tcs = new TaskCompletionSource(); - var errorOutput = new StringBuilder(); - var startupTimer = Stopwatch.StartNew(); - string CapturedErrors() - { - lock (errorOutput) return errorOutput.ToString(); - } + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var diagnostics = new StartupDiagnostics(); + var elapsed = Stopwatch.StartNew(); process.OutputDataReceived += (_, e) => { if (e.Data == null) return; + diagnostics.AppendOutput(e.Data); var match = Regex.Match(e.Data, @"Listening: (?http://[^\s]+)\s+(?\{.*\})$"); if (!match.Success) { @@ -92,7 +88,7 @@ string CapturedErrors() process.ErrorDataReceived += (_, e) => { if (e.Data == null) return; - lock (errorOutput) errorOutput.AppendLine(e.Data); + diagnostics.AppendError(e.Data); Console.Error.WriteLine(e.Data); }; @@ -112,21 +108,56 @@ string CapturedErrors() { if (!tcs.Task.IsCompleted) { - tcs.TrySetException(new Exception($"Proxy exited before listening with code {process.ExitCode}: {CapturedErrors()}")); + tcs.TrySetException(new Exception( + $"Proxy exited before readiness. {diagnostics.Describe(process.Id, elapsed.Elapsed, process.ExitCode)}")); } }); // Use longer timeout on Windows due to slower process startup var timeoutSeconds = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 30 : 10; using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)); - cts.Token.Register(() => tcs.TrySetException(new TimeoutException( - $"Timeout waiting for proxy after {startupTimer.ElapsedMilliseconds}ms " + - $"(exited: {process.HasExited}; stderr: {CapturedErrors()})"))); + using var registration = cts.Token.Register(() => tcs.TrySetException(new TimeoutException( + $"Timeout waiting for proxy. {diagnostics.Describe(process.Id, elapsed.Elapsed, process.HasExited ? process.ExitCode : null)}"))); return await tcs.Task; } } + internal sealed class StartupDiagnostics + { + private const int OutputLimit = 4096; + private readonly object _gate = new(); + private readonly StringBuilder _stdout = new(); + private readonly StringBuilder _stderr = new(); + + public void AppendOutput(string line) => Append(_stdout, line); + + public void AppendError(string line) => Append(_stderr, line); + + private void Append(StringBuilder output, string line) + { + lock (_gate) + { + output.Append(line, Math.Max(0, line.Length - OutputLimit), Math.Min(line.Length, OutputLimit)); + output.AppendLine(); + if (output.Length > OutputLimit) + { + output.Remove(0, output.Length - OutputLimit); + } + } + } + + public string Describe(int processId, TimeSpan elapsed, int? exitCode) + { + lock (_gate) + { + var state = exitCode is int code ? $"exited with code {code}" : "running"; + return $"PID {processId}, {state}, elapsed {elapsed.TotalSeconds:F1}s." + + $"\n--- stdout tail ---\n{_stdout}\n--- stderr tail ---\n{_stderr}"; + } + } + } + public async Task StopAsync(bool skipWritingCache = false) { if (_startupTask != null) @@ -256,7 +287,10 @@ public record CopilotUserQuotaSnapshot( public record ParsedHttpExchange( ChatCompletionRequest Request, ChatCompletionResponse? Response, - Dictionary? RequestHeaders); + Dictionary? RequestHeaders, + CompactionProviderUsage? CompactionUsage = null); + +public record CompactionProviderUsage(string InteractionId, string Summary, int ResponseCount, long? InputTokens); public record ChatCompletionRequest( string Model, diff --git a/dotnet/test/Harness/TestHelper.cs b/dotnet/test/Harness/TestHelper.cs index 173a5f45f5..1a7773dc21 100644 --- a/dotnet/test/Harness/TestHelper.cs +++ b/dotnet/test/Harness/TestHelper.cs @@ -141,7 +141,8 @@ public static string ExtensionLaunchMarkers(string homeDir, string extensionId) var launches = new List(); foreach (var path in Directory.EnumerateFiles(logsDir, "process-*.log")) { - using var reader = new StreamReader(path); + using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete); + using var reader = new StreamReader(stream); if (reader.ReadLine()?.Contains(extensionName, StringComparison.Ordinal) != true) { continue; @@ -149,14 +150,29 @@ public static string ExtensionLaunchMarkers(string homeDir, string extensionId) var markers = new List(); var errors = new HashSet(StringComparer.Ordinal); + var recentLines = new Queue(); while (reader.ReadLine() is { } line) { + if (recentLines.Count == 20) + { + recentLines.Dequeue(); + } + recentLines.Enqueue(line); + if (line.StartsWith("=== ", StringComparison.Ordinal)) { - if (!line.Contains("module=", StringComparison.Ordinal)) + if (line is "=== initializing ===" or "=== ready ===" or "=== peer-registration-failed ===" + or "=== exited-before-ready ===" or "=== startup-timeout ===" + or "=== peer-closed-before-ready ===") { markers.Add(line); } + var exit = Regex.Match(line, @"^=== exit code=(-?\d+|signal) disposition=(startup-failure|crash|stopped-normally|termination-failure) ===$"); + if (exit.Success) + { + var code = exit.Groups[1].Value; + markers.Add($"=== exit code={(code is "0" or "1" or "signal" ? code : "other")} disposition={exit.Groups[2].Value} ==="); + } continue; } @@ -181,28 +197,31 @@ public static string ExtensionLaunchMarkers(string homeDir, string extensionId) } if (line is "[sdk-extension-test] imported" or "[sdk-extension-test] joining" - or "[sdk-extension-test] joined" or "[sdk-extension-test] logged" - || Regex.IsMatch(line, @"^\[sdk-extension-test\] join failed code=(?:-?\d+|none)$")) + or "[sdk-extension-test] joined" or "[sdk-extension-test] logged") { markers.Add(line); } - - var code = Regex.Match(line, @"\b(?:ERR_[A-Z0-9_]+|ECONNRESET|ECONNREFUSED|EPIPE|ENOENT|ETIMEDOUT)\b"); - if (code.Success) - { - errors.Add(code.Value); - } - var type = Regex.Match(line, @"(? 0) { summary += $"; error categories: {string.Join(", ", errors.OrderBy(error => error, StringComparer.Ordinal))}"; } + var recentErrors = new HashSet(StringComparer.Ordinal); + foreach (var line in recentLines) + { + AddErrorCategories(line, recentErrors); + } + if (recentErrors.Count > 0) + { + summary += $"; recent error categories: {string.Join(", ", recentErrors.OrderBy(error => error, StringComparer.Ordinal))}"; + } launches.Add(summary.Length == 0 ? "" : summary); } @@ -213,4 +232,20 @@ public static string ExtensionLaunchMarkers(string homeDir, string extensionId) return $""; } } + + private static void AddErrorCategories(string line, HashSet errors) + { + var code = Regex.Match(line, @"\b(?:ERR_[A-Z0-9_]+|ECONNRESET|ECONNREFUSED|EPIPE|ENOENT|ETIMEDOUT)\b"); + if (code.Success) + { + errors.Add(code.Value.StartsWith("ERR_", StringComparison.Ordinal) && code.Value != "ERR_INVALID_ARG_TYPE" + ? "Node.js error" + : code.Value); + } + var type = Regex.Match(line, @"(? { AutoTier.Balance, custom }.Count); + Assert.Equal("\"BALANCE\"", JsonSerializer.Serialize(custom, JsonOptions)); + Assert.Equal(custom, JsonSerializer.Deserialize("\"BALANCE\"", JsonOptions)); + Assert.Equal("balance", AutoTier.Balance.Value); + + var rpc = JsonSerializer.Deserialize("""{"modelId":"auto","autoTier":"BALANCE"}""", JsonOptions); + Assert.NotNull(rpc); + Assert.Equal(custom, rpc.AutoTier); + Assert.NotEqual(AutoTier.Balance, rpc.AutoTier); + } + + [Fact] + public void CaseDistinctRecommendationIdsRemainDistinct() + { + var custom = new RecommendedAutoTier("BALANCE"); + Assert.NotEqual(RecommendedAutoTier.Balance, custom); + Assert.Equal(2, new HashSet { RecommendedAutoTier.Balance, custom }.Count); + Assert.Equal("\"BALANCE\"", JsonSerializer.Serialize(custom, JsonOptions)); + Assert.Equal(custom, JsonSerializer.Deserialize("\"BALANCE\"", JsonOptions)); + } +} diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.AhpHost.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.AhpHost.cs index f0b584af7b..9fdea1bc92 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.AhpHost.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.AhpHost.cs @@ -74,7 +74,7 @@ public async Task Ahp_Snapshots_Transport_Settings_Before_Connecting() } }; var local = new HostLocalServerOptions { Hostname = "127.0.0.1", Port = 0, Token = "original", RequireConnectionToken = true }; - var github = new HostGitHubEnvironmentOptions { Name = "original", ComputeId = "compute" }; + var github = new HostGitHubEnvironmentOptions { Name = "original", ComputeId = "compute", RequireConnectionBinding = false }; var pending = client.StartAhpHostAsync(new() { LocalServer = local, GitHubEnvironment = github }); await connecting.Task.WaitAsync(TimeSpan.FromSeconds(10)); try @@ -85,6 +85,7 @@ public async Task Ahp_Snapshots_Transport_Settings_Before_Connecting() local.RequireConnectionToken = false; github.Name = ""; github.ComputeId = ""; + github.RequireConnectionBinding = true; } finally { @@ -100,6 +101,7 @@ public async Task Ahp_Snapshots_Transport_Settings_Before_Connecting() var githubSettings = request.GetProperty("githubEnvironment"); Assert.Equal("original", githubSettings.GetProperty("name").GetString()); Assert.Equal("compute", githubSettings.GetProperty("computeId").GetString()); + Assert.False(githubSettings.GetProperty("requireConnectionBinding").GetBoolean()); } [Theory] @@ -112,10 +114,12 @@ public async Task Ahp_Forwards_Explicit_Transports_And_Optional_Readiness(bool l await using var client = new CopilotClient(new() { Connection = RuntimeConnection.ForUri(server.Url) }); await using var host = await client.StartAhpHostAsync(new() { + ComputeId = "compute", LocalServer = local ? new() { Hostname = "127.0.0.1", Port = 0, Token = "test-token", RequireConnectionToken = true } : null, GitHubEnvironment = github ? new() { Name = "test host", ComputeId = "compute" } : null }); var request = Assert.Single(server.Requests, request => request.Method == "host.start").Params; + Assert.Equal("compute", request.GetProperty("computeId").GetString()); Assert.Equal(local, request.TryGetProperty("localServer", out var localSettings) && localSettings.ValueKind == JsonValueKind.Object); Assert.Equal(github, request.TryGetProperty("githubEnvironment", out var githubSettings) && githubSettings.ValueKind == JsonValueKind.Object); foreach (var oldField in new[] { "hostname", "port", "token", "requireConnectionToken" }) @@ -136,6 +140,60 @@ public async Task Ahp_Forwards_Explicit_Transports_And_Optional_Readiness(bool l Assert.Null(host.Pid); } + [Fact] + public async Task Ahp_Legacy_Positional_Start_And_Additive_Request_Overloads() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new() { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + var local = new HostLocalServerOptions { Port = 12345 }; + var github = new HostGitHubEnvironmentOptions { Name = "host", ComputeId = "compute" }; + + await client.Rpc.Host.StartAsync("legacy", local, github, true, false, CancellationToken.None); + await client.Rpc.Host.StartAsync("defaults"); + await client.Rpc.Host.StartAsync(new HostStartRequest + { + HostId = "request", + ComputeId = "compute", + LocalServer = local, + GitHubEnvironment = github, + SessionFactory = true, + ResumeFactory = false + }, CancellationToken.None); + + var requests = server.Requests.Where(request => request.Method == "host.start").ToArray(); + Assert.Equal(3, requests.Length); + var legacy = requests[0].Params; + Assert.Equal("legacy", legacy.GetProperty("hostId").GetString()); + Assert.False(legacy.TryGetProperty("computeId", out _)); + Assert.Equal("defaults", requests[1].Params.GetProperty("hostId").GetString()); + Assert.False(requests[1].Params.TryGetProperty("localServer", out _)); + var additive = requests[2].Params; + Assert.Equal("request", additive.GetProperty("hostId").GetString()); + Assert.Equal("compute", additive.GetProperty("computeId").GetString()); + foreach (var parameters in new[] { legacy, additive }) + { + Assert.Equal(12345, parameters.GetProperty("localServer").GetProperty("port").GetInt32()); + Assert.Equal("compute", parameters.GetProperty("githubEnvironment").GetProperty("computeId").GetString()); + Assert.True(parameters.GetProperty("sessionFactory").GetBoolean()); + Assert.False(parameters.GetProperty("resumeFactory").GetBoolean()); + } + } + + [Fact] + public async Task Ahp_List_Sessions_Uses_Owning_Host() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new() { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var host = await client.StartAhpHostAsync(new() { LocalServer = new() }); + + var result = await host.ListSessionsAsync(); + + Assert.Empty(result.Sessions); + var request = Assert.Single(server.Requests, request => request.Method == "host.listSessions").Params; + Assert.Equal(host.HostId, request.GetProperty("hostId").GetString()); + } + [Theory] [InlineData(true)] [InlineData(false)] diff --git a/dotnet/test/Unit/ClientSessionLifetimeTests.cs b/dotnet/test/Unit/ClientSessionLifetimeTests.cs index c574864185..c3ea694395 100644 --- a/dotnet/test/Unit/ClientSessionLifetimeTests.cs +++ b/dotnet/test/Unit/ClientSessionLifetimeTests.cs @@ -9,6 +9,7 @@ using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; using System.Text; using System.Text.Json; using GitHub.Copilot.Rpc; @@ -23,6 +24,40 @@ public sealed partial class ClientSessionLifetimeTests { private sealed record RpcRequestRecord(string Method, JsonElement Params); + [Theory] + [InlineData(false, true)] + [InlineData(false, false)] + [InlineData(false, null)] + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(true, null)] + public async Task ImageGeneration_ForwardsCreateAndResumeOptIn(bool resume, bool? enabled) + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var imageGeneration = enabled is null ? null : new ImageGenerationConfig { Enabled = enabled }; + await using var session = resume + ? await client.ResumeSessionAsync("image-test", new ResumeSessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + ImageGeneration = imageGeneration + }.Clone()) + : await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + ImageGeneration = imageGeneration + }.Clone()); + var request = Assert.Single(server.Requests, r => r.Method == (resume ? "session.resume" : "session.create")).Params; + if (enabled is null) + { + Assert.False(request.TryGetProperty("imageGeneration", out _), request.ToString()); + } + else + { + Assert.Equal(enabled.Value, request.GetProperty("imageGeneration").GetProperty("enabled").GetBoolean()); + } + } + [Theory] [InlineData(CopilotClientMode.CopilotCli, null, null)] [InlineData(CopilotClientMode.Empty, null, null)] @@ -66,6 +101,154 @@ public async Task ResumeTranscriptRecovery_PreservesOverridesAndProjectsReport(C Assert.Null(unrepaired.TranscriptRecovery); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task SubagentStartHook_RegistersOnCreateAndResume_AndMapsInputAndOutput(bool resume) + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var calls = new List<(SubagentStartHookInput Input, string InvocationSessionId)>(); + var hooks = new SessionHooks + { + OnSubagentStart = (input, invocation) => + { + calls.Add((input, invocation.SessionId)); + return Task.FromResult(new SubagentStartHookOutput + { + AdditionalContext = "Context for the child" + }); + } + }; + await using var session = resume + ? await client.ResumeSessionAsync("resumed-session", new ResumeSessionConfig { Hooks = hooks }) + : await client.CreateSessionAsync(new SessionConfig { Hooks = hooks }); + + var registration = Assert.Single(server.Requests, r => r.Method == (resume ? "session.resume" : "session.create")); + Assert.True(registration.Params.GetProperty("hooks").GetBoolean()); + + var input = new Dictionary + { + ["sessionId"] = session.SessionId, + ["timestamp"] = 1_730_000_000_000L, + ["cwd"] = "workdir", + ["transcriptPath"] = "parent.jsonl", + ["agentName"] = "explore", + ["agentDisplayName"] = "Explorer", + ["agentDescription"] = "Reads files" + }; + var request = new Dictionary + { + ["sessionId"] = session.SessionId, + ["hookType"] = "subagentStart", + ["input"] = input + }; + var result = await server.SendRequestAsync("hooks.invoke", request).WaitAsync(TimeSpan.FromSeconds(5)); + var output = result.GetProperty("output"); + var property = Assert.Single(output.EnumerateObject()); + Assert.Equal("additionalContext", property.Name); + Assert.Equal("Context for the child", property.Value.GetString()); + + var (first, invocationSessionId) = Assert.Single(calls); + Assert.Equal(session.SessionId, invocationSessionId); + Assert.Equal(session.SessionId, first.SessionId); + Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1_730_000_000_000L), first.Timestamp); + Assert.Equal("workdir", first.WorkingDirectory); + Assert.Equal("parent.jsonl", first.TranscriptPath); + Assert.Equal("explore", first.AgentName); + Assert.Equal("Explorer", first.AgentDisplayName); + Assert.Equal("Reads files", first.AgentDescription); + + input.Remove("agentDisplayName"); + input.Remove("agentDescription"); + await server.SendRequestAsync("hooks.invoke", request).WaitAsync(TimeSpan.FromSeconds(5)); + Assert.Equal(2, calls.Count); + Assert.Null(calls[1].Input.AgentDisplayName); + Assert.Null(calls[1].Input.AgentDescription); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task SubagentStopHook_RegistersOnCreateAndResume_AndMapsInputAndBothOutputs(bool resume) + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var calls = new List<(SubagentStopHookInput Input, string InvocationSessionId)>(); + var block = true; + var hooks = new SessionHooks + { + OnSubagentStop = (input, invocation) => + { + calls.Add((input, invocation.SessionId)); + return Task.FromResult(block + ? new SubagentStopHookOutput { Decision = "block", Reason = "Continue reading" } + : new SubagentStopHookOutput { ModifiedResponse = "Replaced child response" }); + } + }; + await using var session = resume + ? await client.ResumeSessionAsync("resumed-session", new ResumeSessionConfig { Hooks = hooks }) + : await client.CreateSessionAsync(new SessionConfig { Hooks = hooks }); + + var registration = Assert.Single(server.Requests, r => r.Method == (resume ? "session.resume" : "session.create")); + Assert.True(registration.Params.GetProperty("hooks").GetBoolean()); + + var input = new Dictionary + { + ["sessionId"] = session.SessionId, + ["timestamp"] = 1_730_000_000_000L, + ["cwd"] = "workdir", + ["transcriptPath"] = "parent.jsonl", + ["agentName"] = "explore", + ["agentDisplayName"] = "Explorer", + ["agentDescription"] = "Reads files", + ["agentId"] = "read-file", + ["agentType"] = "explore", + ["stopReason"] = "end_turn", + ["response"] = "Hello from subagent test!" + }; + var request = new Dictionary + { + ["sessionId"] = session.SessionId, + ["hookType"] = "subagentStop", + ["input"] = input + }; + var blocked = await server.SendRequestAsync("hooks.invoke", request).WaitAsync(TimeSpan.FromSeconds(5)); + var blockOutput = blocked.GetProperty("output"); + Assert.Equal(2, blockOutput.EnumerateObject().Count()); + Assert.Equal("block", blockOutput.GetProperty("decision").GetString()); + Assert.Equal("Continue reading", blockOutput.GetProperty("reason").GetString()); + Assert.False(blockOutput.TryGetProperty("modifiedResponse", out _)); + + var (first, invocationSessionId) = Assert.Single(calls); + Assert.Equal(session.SessionId, invocationSessionId); + Assert.Equal(session.SessionId, first.SessionId); + Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1_730_000_000_000L), first.Timestamp); + Assert.Equal("workdir", first.WorkingDirectory); + Assert.Equal("parent.jsonl", first.TranscriptPath); + Assert.Equal("explore", first.AgentName); + Assert.Equal("Explorer", first.AgentDisplayName); + Assert.Equal("Reads files", first.AgentDescription); + Assert.Equal("read-file", first.AgentId); + Assert.Equal("explore", first.AgentType); + Assert.Equal("end_turn", first.StopReason); + Assert.Equal("Hello from subagent test!", first.Response); + + input.Remove("agentId"); + input.Remove("agentDisplayName"); + input.Remove("agentDescription"); + block = false; + var modified = await server.SendRequestAsync("hooks.invoke", request).WaitAsync(TimeSpan.FromSeconds(5)); + var modifiedOutput = modified.GetProperty("output"); + var property = Assert.Single(modifiedOutput.EnumerateObject()); + Assert.Equal("modifiedResponse", property.Name); + Assert.Equal("Replaced child response", property.Value.GetString()); + Assert.Equal(2, calls.Count); + Assert.Null(calls[1].Input.AgentId); + Assert.Null(calls[1].Input.AgentDisplayName); + Assert.Null(calls[1].Input.AgentDescription); + } + [Theory] [InlineData("static")] [InlineData("")] @@ -1308,6 +1491,140 @@ public async Task CreateSessionAsync_Registers_McpAuth_Interest_After_Cloud_Crea }); } + [Fact] + public async Task CreateSessionAsync_Preserves_Server_Assigned_Session_When_McpAuth_Interest_Fails() + { + await using var server = await FakeCopilotServer.StartAsync(); + server.FailMcpAuthInterestRegistration = true; + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + + var error = await Assert.ThrowsAsync(() => client.CreateSessionAsync(new SessionConfig + { + Cloud = new CloudSessionOptions + { + Repository = new CloudSessionRepository { Owner = "github", Name = "copilot-sdk", Branch = "main" } + }, + OnMcpAuthRequest = _ => Task.FromResult(McpAuthResult.Cancel()) + })); + + Assert.Contains("interest registration failed", error.Message); + var create = Assert.Single(server.Requests, request => request.Method == "session.create"); + Assert.False(create.Params.TryGetProperty("sessionId", out _)); + Assert.NotNull(server.LastSessionId); + var interest = Assert.Single(server.Requests, request => request.Method == "session.eventLog.registerInterest"); + Assert.Equal(server.LastSessionId, interest.Params.GetProperty("sessionId").GetString()); + Assert.DoesNotContain(server.Requests, request => request.Method == "session.delete"); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task CreateSessionAsync_Deletes_Server_Assigned_Session_When_Binary_Provider_Validation_Fails(bool failSessionDelete) + { + await using var server = await FakeCopilotServer.StartAsync(); + server.FailSessionDelete = failSessionDelete; + server.ResponseFactory = request => request.Method == "sessionFs.setProvider" + ? new Dictionary { ["success"] = true } + : throw new InvalidOperationException($"Unexpected RPC method '{request.Method}'."); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + SessionFs = new SessionFsConfig + { + InitialWorkingDirectory = "/", + SessionStatePath = "/session-state", + Conventions = SessionFsSetProviderConventions.Posix, + Capabilities = new SessionFsSetProviderCapabilities { Binary = true } + } + }); + + var error = await Assert.ThrowsAsync(() => client.CreateSessionAsync(new SessionConfig + { + Cloud = new CloudSessionOptions + { + Repository = new CloudSessionRepository { Owner = "github", Name = "copilot-sdk", Branch = "main" } + }, + CreateSessionFsProvider = _ => new TextOnlySessionFsProvider() + })); + + Assert.Contains("does not implement ISessionFsBinaryProvider", error.Message); + var create = Assert.Single(server.Requests, request => request.Method == "session.create"); + Assert.False(create.Params.TryGetProperty("sessionId", out _)); + var delete = Assert.Single(server.Requests, request => request.Method == "session.delete"); + Assert.NotNull(server.LastSessionId); + Assert.Equal(server.LastSessionId, delete.Params.GetProperty("sessionId").GetString()); + } + + [Fact] + public async Task Binary_SessionFs_Provider_Rejects_Oversized_Bytes_Before_Encoding() + { + var provider = new BinarySessionFsProvider(_ => Task.FromResult(new byte[(64 * 1024 * 1024 - 1024) / 4 * 3 + 1])); + var result = await ((ISessionFsHandler)provider).ReadFileBytesAsync( + new SessionFsReadFileBytesRequest { SessionId = "session", Path = "/image.png" }, CancellationToken.None); + + Assert.Empty(result.Content); + Assert.NotNull(result.Error); + Assert.Equal(SessionFsErrorCode.UNKNOWN, result.Error.Code); + Assert.Contains("binary read limit", result.Error.Message); + } + + [Fact] + public async Task Binary_SessionFs_Provider_Propagates_Cancellation() + { + var provider = new BinarySessionFsProvider(token => Task.FromCanceled(token)); + var cancellationToken = new CancellationToken(canceled: true); + await Assert.ThrowsAnyAsync(() => ((ISessionFsHandler)provider).ReadFileBytesAsync( + new SessionFsReadFileBytesRequest { SessionId = "session", Path = "/image.png" }, cancellationToken)); + } + + [Fact] + public async Task Binary_SessionFs_Provider_Writes_Exact_Bytes_And_Rejects_Invalid_Input() + { + ReadOnlyMemory? received = null; + int? receivedMode = null; + var provider = new BinarySessionFsProvider( + _ => Task.FromResult(Array.Empty()), + (content, mode) => { received = content; receivedMode = mode; return Task.CompletedTask; }); + var handler = (ISessionFsHandler)provider; + var result = await handler.WriteFileBytesAsync( + new SessionFsWriteFileBytesRequest { SessionId = "session", Path = "/image.png", Content = "AP/+AQ==", Mode = 384 }, + CancellationToken.None); + Assert.Null(result); + Assert.Equal(new byte[] { 0, 255, 254, 1 }, received?.ToArray()); + Assert.Equal(384, receivedMode); + + foreach (var content in new[] { "AA==AAAA", "AA==\r\n", new string('A', 64 * 1024 * 1024) }) + { + var error = await handler.WriteFileBytesAsync( + new SessionFsWriteFileBytesRequest { SessionId = "session", Path = "/image.png", Content = content }, + CancellationToken.None); + Assert.NotNull(error); + Assert.Equal(new byte[] { 0, 255, 254, 1 }, received?.ToArray()); + } + } + + private sealed class BinarySessionFsProvider(Func> read, Func, int?, Task>? write = null) + : TextOnlySessionFsProvider, ISessionFsBinaryProvider + { + public Task ReadFileBytesAsync(string path, CancellationToken cancellationToken) => read(cancellationToken); + public Task WriteFileBytesAsync(string path, ReadOnlyMemory content, int? mode, CancellationToken cancellationToken) => + write?.Invoke(content, mode) ?? Task.CompletedTask; + } + + private class TextOnlySessionFsProvider : SessionFsProvider + { + protected override Task ReadFileAsync(string path, CancellationToken cancellationToken) => throw new NotSupportedException(); + protected override Task WriteFileAsync(string path, string content, int? mode, CancellationToken cancellationToken) => throw new NotSupportedException(); + protected override Task AppendFileAsync(string path, string content, int? mode, CancellationToken cancellationToken) => throw new NotSupportedException(); + protected override Task ExistsAsync(string path, CancellationToken cancellationToken) => throw new NotSupportedException(); + protected override Task StatAsync(string path, CancellationToken cancellationToken) => throw new NotSupportedException(); + protected override Task MakeDirectoryAsync(string path, bool recursive, int? mode, CancellationToken cancellationToken) => throw new NotSupportedException(); + protected override Task> ReadDirectoryAsync(string path, CancellationToken cancellationToken) => throw new NotSupportedException(); + protected override Task> ReadDirectoryWithTypesAsync(string path, CancellationToken cancellationToken) => throw new NotSupportedException(); + protected override Task RemoveAsync(string path, bool recursive, bool force, CancellationToken cancellationToken) => throw new NotSupportedException(); + protected override Task RenameAsync(string src, string dest, CancellationToken cancellationToken) => throw new NotSupportedException(); + } + [Fact] public async Task ResumeSessionAsync_Registers_McpAuth_Interest_Only_When_Handler_Configured() { @@ -1509,6 +1826,613 @@ async Task BlockedTool(CancellationToken cancellationToken) } } + [Fact] + public Task ForceStopAsync_Does_Not_Invoke_Remaining_Event_Handlers() => + AssertRemainingEventHandlersStoppedAsync(static (client, _, _) => client.ForceStopAsync()); + + [Fact] + public Task DisposeAsync_Does_Not_Invoke_Remaining_Event_Handlers() => + AssertRemainingEventHandlersStoppedAsync(static (_, session, _) => session.DisposeAsync().AsTask()); + + [Fact] + public Task Unsubscribe_Releases_Unadmitted_Event_Handler_Captures() => + AssertRemainingEventHandlersStoppedAsync(static (_, _, unsubscribe) => + { + unsubscribe(); + return Task.CompletedTask; + }); + + [Fact] + public Task ForceStopAsync_Fails_Pending_SendAndWait_After_Send_Response() => + AssertPendingSendAndWaitStoppedAsync(static (client, _) => client.ForceStopAsync()); + + [Fact] + public Task DisposeAsync_Fails_Pending_SendAndWait_After_Send_Response() => + AssertPendingSendAndWaitStoppedAsync(static (_, session) => session.DisposeAsync().AsTask()); + + [Fact] + public async Task ForceStopAsync_Rejects_Event_Handler_Registration() + { + 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.CreateSessionAsync(new SessionConfig()); + + await client.ForceStopAsync(); + + Assert.Throws(() => session.On(_ => { })); + } + + [Theory] + [InlineData(false, true)] + [InlineData(true, true)] + [InlineData(false, false)] + [InlineData(true, false)] + public async Task Stop_Rejects_And_Retires_Session_Initialized_Before_Registration(bool resume, bool force) + { + await using var server = await FakeCopilotServer.StartAsync(); + server.ResponseFactory = request => request.Method == "sessionFs.setProvider" + ? new Dictionary { ["success"] = true } + : throw new InvalidOperationException($"Unexpected RPC method '{request.Method}'."); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + SessionFs = new SessionFsConfig + { + InitialWorkingDirectory = "/", + SessionStatePath = "/session-state", + Conventions = SessionFsSetProviderConventions.Posix + } + }); + await client.StartAsync(); + using var releaseSetup = new ManualResetEventSlim(); + var setupStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + SessionFsProvider CreateProvider(CopilotSession session) + { + setupStarted.SetResult(session); + releaseSetup.Wait(); + return new TextOnlySessionFsProvider(); + } + var creation = Task.Run(() => resume + ? client.ResumeSessionAsync("late-registration", new ResumeSessionConfig { CreateSessionFsProvider = CreateProvider }) + : client.CreateSessionAsync(new SessionConfig { CreateSessionFsProvider = CreateProvider })); + try + { + var session = await setupStarted.Task.WaitAsync(TimeSpan.FromSeconds(5)); + if (force) + { + await client.ForceStopAsync().WaitAsync(TimeSpan.FromSeconds(5)); + } + else + { + await client.StopAsync().WaitAsync(TimeSpan.FromSeconds(5)); + } + releaseSetup.Set(); + var error = await Assert.ThrowsAsync(() => creation.WaitAsync(TimeSpan.FromSeconds(5))); + Assert.Contains("stopped connection", error.Message); + Assert.Throws(() => session.On(_ => { })); + Assert.DoesNotContain(server.Requests, request => request.Method is "session.create" or "session.resume"); + } + finally + { + releaseSetup.Set(); + await Record.ExceptionAsync(() => creation.WaitAsync(TimeSpan.FromSeconds(5))); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task Stop_Closes_Session_Admission_Before_Transport_Cleanup(bool resume) + { + await using var server = await FakeCopilotServer.StartAsync(); + server.ResponseFactory = request => request.Method == "sessionFs.setProvider" + ? new Dictionary { ["success"] = true } + : throw new InvalidOperationException($"Unexpected RPC method '{request.Method}'."); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + SessionFs = new SessionFsConfig + { + InitialWorkingDirectory = "/", + SessionStatePath = "/session-state", + Conventions = SessionFsSetProviderConventions.Posix + } + }); + _ = await client.CreateSessionAsync(new SessionConfig { CreateSessionFsProvider = _ => new TextOnlySessionFsProvider() }); + server.DelayDestroy(); + var stop = client.StopAsync(); + await server.DestroyStarted.WaitAsync(TimeSpan.FromSeconds(5)); + try + { + CopilotSession? initialized = null; + SessionFsProvider CreateProvider(CopilotSession session) + { + initialized = session; + return new TextOnlySessionFsProvider(); + } + var creation = resume + ? client.ResumeSessionAsync("late-registration", new ResumeSessionConfig { CreateSessionFsProvider = CreateProvider }) + : client.CreateSessionAsync(new SessionConfig { CreateSessionFsProvider = CreateProvider }); + var error = await Assert.ThrowsAsync(() => creation.WaitAsync(TimeSpan.FromSeconds(5))); + Assert.Contains("stopped connection", error.Message); + Assert.NotNull(initialized); + Assert.Throws(() => initialized.On(_ => { })); + Assert.False(stop.IsCompleted); + } + finally + { + await client.ForceStopAsync().WaitAsync(TimeSpan.FromSeconds(5)); + await stop.WaitAsync(TimeSpan.FromSeconds(5)); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task Cloud_Create_Cleanup_Does_Not_Reconnect_During_Stop(bool dispose) + { + await using var server = await FakeCopilotServer.StartAsync(); + server.ResponseFactory = request => request.Method == "sessionFs.setProvider" + ? new Dictionary { ["success"] = true } + : throw new InvalidOperationException($"Unexpected RPC method '{request.Method}'."); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + SessionFs = new SessionFsConfig + { + InitialWorkingDirectory = "/", + SessionStatePath = "/session-state", + Conventions = SessionFsSetProviderConventions.Posix + } + }); + await client.StartAsync(); + using var process = StartExitedProcess(); + await ReplaceConnectionResourcesAsync(client, process); + using var releaseSetup = new ManualResetEventSlim(); + using var reconnectCancellation = new CancellationTokenSource(); + var reconnect = server.ObserveReconnectAsync(reconnectCancellation.Token); + var setupStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var shutdownStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var releaseShutdown = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + server.BeforeResponseAsync = async (request, token) => + { + if (request.Method == "runtime.shutdown") + { + shutdownStarted.TrySetResult(); + await releaseShutdown.Task.WaitAsync(token); + } + }; + var creation = client.CreateSessionAsync(new SessionConfig + { + Cloud = new CloudSessionOptions + { + Repository = new CloudSessionRepository { Owner = "github", Name = "copilot-sdk", Branch = "main" } + }, + CreateSessionFsProvider = session => + { + setupStarted.TrySetResult(session); + releaseSetup.Wait(); + return new TextOnlySessionFsProvider(); + } + }); + Task? stop = null; + Exception? failure = null; + var timeout = TimeSpan.FromSeconds(5); + try + { + var session = await setupStarted.Task.WaitAsync(timeout); + stop = dispose ? client.DisposeAsync().AsTask() : client.StopAsync(); + await shutdownStarted.Task.WaitAsync(timeout); + releaseSetup.Set(); + releaseShutdown.TrySetResult(); + + var error = await Assert.ThrowsAsync(() => creation.WaitAsync(timeout)); + Assert.Contains("stopped connection", error.Message); + await stop.WaitAsync(timeout); + Assert.Throws(() => session.On(_ => { })); + Assert.False(server.ReconnectObserved, "Cloud-create cleanup reconnected after session admission closed."); + } + catch (Exception error) + { + failure = error; + } + finally + { + releaseSetup.Set(); + releaseShutdown.TrySetResult(); + foreach (var operation in new Task?[] { creation, stop }) + { + if (operation is null) continue; + try + { + await operation.WaitAsync(timeout); + } + catch (IOException) when (ReferenceEquals(operation, creation)) + { + // The rejected creation is the expected result, not a cleanup failure. + } + catch (Exception cleanupError) + { + failure = failure is null ? cleanupError : new AggregateException(failure, cleanupError); + } + } + reconnectCancellation.Cancel(); + try + { + await reconnect.WaitAsync(timeout); + } + catch (OperationCanceledException) when (reconnectCancellation.IsCancellationRequested) + { + } + catch (Exception cleanupError) + { + failure = failure is null ? cleanupError : new AggregateException(failure, cleanupError); + } + } + if (failure is not null) + { + ExceptionDispatchInfo.Capture(failure).Throw(); + } + } + + [Theory] + [InlineData("hooks", true)] + [InlineData("mcp", true)] + [InlineData("bearer", true)] + [InlineData("transforms", true)] + [InlineData("tools", true)] + [InlineData("commands", true)] + [InlineData("permission", true)] + [InlineData("input", true)] + [InlineData("hooks", false)] + [InlineData("mcp", false)] + [InlineData("bearer", false)] + [InlineData("transforms", false)] + [InlineData("tools", false)] + [InlineData("commands", false)] + [InlineData("permission", false)] + [InlineData("input", false)] + public async Task Retirement_Releases_Retained_Session_Callback_Captures(string registration, bool forceStop) + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var (session, capture) = await CreateCapturedSessionAsync(client, registration); + await using var retainedSession = session; + ForceCollect(); + Assert.True(CaptureIsAlive(capture)); + + if (forceStop) + { + await client.ForceStopAsync(); + } + else + { + await session.DisposeAsync(); + } + ForceCollect(); + + Assert.False(CaptureIsAlive(capture)); + GC.KeepAlive(session); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static bool CaptureIsAlive(WeakReference capture) => capture.TryGetTarget(out _); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static async Task<(CopilotSession Session, WeakReference Capture)> CreateCapturedSessionAsync( + CopilotClient client, string registration) + { + var capture = new object(); + var weak = new WeakReference(capture); + var config = new SessionConfig { OnPermissionRequest = PermissionHandler.ApproveAll }; + switch (registration) + { + case "hooks": + config.Hooks = new SessionHooks + { + OnSessionStart = (_, _) => + { + GC.KeepAlive(capture); + return Task.FromResult(null); + } + }; + break; + case "mcp": + config.OnMcpAuthRequest = _ => + { + GC.KeepAlive(capture); + return Task.FromResult(McpAuthResult.Cancel()); + }; + break; + case "bearer": + config.Model = "test-model"; + config.Provider = new ProviderConfig + { + BaseUrl = "http://127.0.0.1", + BearerTokenProvider = _ => + { + GC.KeepAlive(capture); + return Task.FromResult("test-token"); + } + }; + break; + case "transforms": + config.SystemMessage = new SystemMessageConfig + { + Mode = SystemMessageMode.Customize, + Sections = new Dictionary + { + [SystemMessageSection.Tone] = new() + { + Transform = content => + { + GC.KeepAlive(capture); + return Task.FromResult(content); + } + } + } + }; + break; + case "tools": + config.Tools = [AIFunctionFactory.Create(() => + { + GC.KeepAlive(capture); + return "test-result"; + }, "captured_tool")]; + break; + case "commands": + config.Commands = [new CommandDefinition + { + Name = "captured", + Handler = _ => + { + GC.KeepAlive(capture); + return Task.CompletedTask; + } + }]; + break; + case "permission": + config.OnPermissionRequest = (_, _) => + { + GC.KeepAlive(capture); + return Task.FromResult(PermissionDecision.UserNotAvailable()); + }; + break; + case "input": + config.OnUserInputRequest = (_, _) => + { + GC.KeepAlive(capture); + return Task.FromResult(new UserInputResponse { Answer = "test-answer" }); + }; + break; + default: + throw new ArgumentOutOfRangeException(nameof(registration), registration, "Unknown callback registration."); + } + return (await client.CreateSessionAsync(config), weak); + } + + [Fact] + public async Task DisposeAsync_Keeps_Hooks_Through_Detach_Acknowledgement() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var detachStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var releaseDetach = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var hookStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var hooks = new SessionHooks + { + OnSessionEnd = (_, _) => + { + hookStarted.TrySetResult(); + return Task.FromResult(null); + } + }; + var endHook = hooks.OnSessionEnd; + await using var session = await client.CreateSessionAsync(new SessionConfig { Hooks = hooks }); + server.BeforeResponseAsync = async (request, token) => + { + if (request.Method == "session.detach") + { + detachStarted.TrySetResult(); + await releaseDetach.Task.WaitAsync(token); + } + }; + var timeout = TimeSpan.FromSeconds(5); + var disposal = session.DisposeAsync().AsTask(); + Task? hookRequest = null; + Exception? bodyFailure = null; + try + { + await detachStarted.Task.WaitAsync(timeout); + hookRequest = server.SendRequestAsync("hooks.invoke", new Dictionary + { + ["sessionId"] = session.SessionId, + ["hookType"] = "sessionEnd", + ["input"] = new Dictionary + { + ["sessionId"] = session.SessionId, + ["timestamp"] = 1_730_000_000_000L, + ["cwd"] = "workdir", + ["reason"] = "complete" + } + }); + await hookStarted.Task.WaitAsync(timeout); + } + catch (Exception error) + { + bodyFailure = error; + } + finally + { + releaseDetach.TrySetResult(); + foreach (var operation in new Task?[] { disposal, hookRequest }) + { + if (operation is null) continue; + try + { + await operation.WaitAsync(timeout); + } + catch (Exception cleanupError) + { + bodyFailure = bodyFailure is null ? cleanupError : new AggregateException(bodyFailure, cleanupError); + } + } + } + if (bodyFailure is not null) + { + ExceptionDispatchInfo.Capture(bodyFailure).Throw(); + } + Assert.Same(endHook, hooks.OnSessionEnd); + } + + [Fact] + public async Task DisposeAsync_Releases_Event_Handler_Captures() + { + 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.CreateSessionAsync(new SessionConfig()); + var capture = RegisterCapturedEventHandler(session, out _); + + await session.DisposeAsync(); + ForceCollect(); + + Assert.False(capture.TryGetTarget(out _)); + GC.KeepAlive(session); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static WeakReference RegisterCapturedEventHandler(CopilotSession session, out IDisposable subscription) + { + var capture = new object(); + subscription = session.On(_ => GC.KeepAlive(capture)); + return new WeakReference(capture); + } + + private static async Task AssertPendingSendAndWaitStoppedAsync(Func stop) + { + 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.CreateSessionAsync(new SessionConfig()); + using var cancellation = new CancellationTokenSource(); + var timeout = TimeSpan.FromSeconds(5); + var responseDrained = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + server.AfterResponseAsync = async (request, token) => + { + if (request.Method == "session.send") + { + await server.SendAndDrainSessionEventAsync(session, "assistant.message", new() + { + ["messageId"] = "message-1", + ["content"] = "WAITING_FOR_IDLE" + }, timeout, token); + responseDrained.TrySetResult(); + } + }; + var pending = session.SendAndWaitAsync(new MessageOptions { Prompt = "wait until idle" }, cancellationToken: cancellation.Token); + Exception? bodyFailure = null; + try + { + await responseDrained.Task.WaitAsync(timeout); + await stop(client, session).WaitAsync(timeout); + await Assert.ThrowsAsync(() => pending.WaitAsync(timeout)); + } + catch (Exception error) + { + bodyFailure = error; + } + finally + { + try + { + cancellation.Cancel(); + await pending.WaitAsync(timeout); + } + catch (Exception error) when (error is IOException or OperationCanceledException) + { + // The stopped operation may fault, or the owned cleanup cancellation may end it. + } + catch (Exception cleanupError) + { + bodyFailure = bodyFailure is null ? cleanupError : new AggregateException(bodyFailure, cleanupError); + } + } + if (bodyFailure is not null) + { + ExceptionDispatchInfo.Capture(bodyFailure).Throw(); + } + } + + private static async Task AssertRemainingEventHandlersStoppedAsync(Func stop) + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + // Channel closure may queue a final continuation after disposal; an idle pair owns no thread. + var scheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, maxConcurrencyLevel: 1); + var factory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, scheduler.ExclusiveScheduler); + using var releaseHandler = new ManualResetEventSlim(); + var handlerStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var laterHandlerCalls = 0; + CopilotSession? session = null; + Exception? bodyFailure = null; + try + { + // Resume starts the event consumer on the caller's scheduler. A queued task fences + // that consumer's current dispatch without relying on a delay or private SDK state. + session = await factory.StartNew(() => client.ResumeSessionAsync("force-stop-event-handler", new ResumeSessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll + })).Unwrap(); + using var first = session.On(_ => + { + handlerStarted.TrySetResult(TaskScheduler.Current); + releaseHandler.Wait(); + }); + using var second = session.On(_ => Interlocked.Increment(ref laterHandlerCalls)); + var capture = RegisterCapturedEventHandler(session, out var capturedSubscription); + using var captureSubscription = capturedSubscription; + await server.SendSessionEventAsync(session.SessionId, "assistant.message", new() + { + ["messageId"] = "force-stop-dispatch", + ["content"] = "STOP_EVENT_DISPATCH" + }); + Assert.Same(scheduler.ExclusiveScheduler, await handlerStarted.Task.WaitAsync(TimeSpan.FromSeconds(5))); + await stop(client, session, () => + { + second.Dispose(); + captureSubscription.Dispose(); + }).WaitAsync(TimeSpan.FromSeconds(5)); + ForceCollect(); + Assert.False(capture.TryGetTarget(out _)); + releaseHandler.Set(); + await factory.StartNew(() => { }).WaitAsync(TimeSpan.FromSeconds(5)); + Assert.Equal(0, Volatile.Read(ref laterHandlerCalls)); + } + catch (Exception error) + { + bodyFailure = error; + } + finally + { + releaseHandler.Set(); + try + { + await factory.StartNew(() => { }).WaitAsync(TimeSpan.FromSeconds(5)); + if (session is not null) + { + await session.DisposeAsync(); + } + } + catch (Exception cleanupError) + { + bodyFailure = bodyFailure is null ? cleanupError : new AggregateException(bodyFailure, cleanupError); + } + } + if (bodyFailure is not null) + { + ExceptionDispatchInfo.Capture(bodyFailure).Throw(); + } + } + [Fact] public async Task ForceStopAsync_Does_Not_Start_Late_External_Tool() { @@ -1606,6 +2530,86 @@ async Task BlockedTool(CancellationToken cancellationToken) } } + [Fact] + public async Task ExternalTool_Invocation_Names_The_Sub_Agent_And_Request_Behind_The_Call() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var invocations = new ConcurrentQueue(); + var tool = CopilotTool.DefineTool( + (ToolInvocation invocation) => + { + invocations.Enqueue(invocation); + return "ok"; + }, + factoryOptions: new AIFunctionFactoryOptions { Name = "calc" }); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + Tools = [tool], + OnPermissionRequest = PermissionHandler.ApproveAll + }); + server.ClearRequests(); + + DispatchEvent(session, new ExternalToolRequestedEvent + { + AgentId = "task-agent-1", + Data = new ExternalToolRequestedData + { + RequestId = "req-sub-agent", + SessionId = session.SessionId, + ToolCallId = "tc-sub-agent", + ToolName = "calc" + } + }); + await WaitForRequestAsync(server, "session.tools.handlePendingToolCall"); + server.ClearRequests(); + DispatchEvent(session, new ExternalToolRequestedEvent + { + Data = new ExternalToolRequestedData + { + RequestId = "req-root", + SessionId = session.SessionId, + ToolCallId = "tc-root", + ToolName = "calc" + } + }); + await WaitForRequestAsync(server, "session.tools.handlePendingToolCall"); + server.ClearRequests(); + DispatchEvent(session, new ExternalToolRequestedEvent + { + AgentId = "", + Data = new ExternalToolRequestedData + { + RequestId = "req-blank-agent", + SessionId = session.SessionId, + ToolCallId = "tc-blank-agent", + ToolName = "calc" + } + }); + await WaitForRequestAsync(server, "session.tools.handlePendingToolCall"); + + Assert.Collection( + invocations, + subAgentCall => + { + Assert.Equal("tc-sub-agent", subAgentCall.ToolCallId); + Assert.Equal("task-agent-1", subAgentCall.AgentId); + Assert.Equal("req-sub-agent", subAgentCall.RequestId); + }, + rootCall => + { + Assert.Equal("tc-root", rootCall.ToolCallId); + Assert.Null(rootCall.AgentId); + Assert.Equal("req-root", rootCall.RequestId); + }, + blankAgentCall => + { + Assert.Equal("tc-blank-agent", blankAgentCall.ToolCallId); + Assert.Null(blankAgentCall.AgentId); + Assert.Equal("req-blank-agent", blankAgentCall.RequestId); + }); + } + private static ExternalToolRequestedEvent ExternalToolRequested(string requestId, string toolName = "blocked_tool") => new() { @@ -2221,7 +3225,8 @@ public async Task SendAndWaitAsync_Waits_For_Earlier_Idle_Handler() using var subscription = session.On(_ => { entered.TrySetResult(); - handlerFinished = release.Wait(TimeSpan.FromSeconds(5)); + release.Wait(); + handlerFinished = true; }); var pending = session.SendAndWaitAsync(new MessageOptions { Prompt = "hello" }); await WaitForRequestAsync(server, "session.send"); @@ -2324,14 +3329,17 @@ public async Task CreateSessionAsync_Serializes_ManagedSettings_Permissions() await using var session = await client.CreateSessionAsync(new SessionConfig { + EnforceManagedModelDefaults = true, ManagedSettings = new ManagedSettings { Permissions = new ManagedSettingsPermissions { DisableBypassPermissionsMode = DisableBypassPermissionsModes.Disable, + DisableAssistedPermissionsMode = true, Deny = ["shell(rm*)"], Ask = ["write"], - Allow = [] + Allow = [], + LimitTo = ["Domain(github.com)"] } }, OnPermissionRequest = (_, invocation) => @@ -2342,12 +3350,17 @@ 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 _)); var permissions = request.Params.GetProperty("managedSettings").GetProperty("permissions"); Assert.Equal("disable", permissions.GetProperty("disableBypassPermissionsMode").GetString()); + Assert.True(permissions.GetProperty("disableAssistedPermissionsMode").GetBoolean()); Assert.Equal("shell(rm*)", Assert.Single(permissions.GetProperty("deny").EnumerateArray()).GetString()); Assert.Equal("write", Assert.Single(permissions.GetProperty("ask").EnumerateArray()).GetString()); Assert.Empty(permissions.GetProperty("allow").EnumerateArray()); + Assert.Equal( + "Domain(github.com)", + Assert.Single(permissions.GetProperty("limitTo").EnumerateArray()).GetString()); DispatchEvent(session, new PermissionRequestedEvent { @@ -2613,6 +3626,7 @@ public async Task ResumeSessionAsync_Serializes_ManagedSettings_Permissions() await using var session = await client.ResumeSessionAsync("session-managed", new ResumeSessionConfig { + EnforceManagedModelDefaults = true, ManagedSettings = new ManagedSettings { Permissions = new ManagedSettingsPermissions @@ -2625,6 +3639,7 @@ 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()); var permissions = request.Params.GetProperty("managedSettings").GetProperty("permissions"); Assert.Equal("shell(rm*)", Assert.Single(permissions.GetProperty("deny").EnumerateArray()).GetString()); } @@ -2717,7 +3732,7 @@ private static Process StartExitedProcess() return process; } - private sealed class FakeCopilotServer : IAsyncDisposable + private sealed partial class FakeCopilotServer : IAsyncDisposable { private readonly TcpListener _listener; private readonly CancellationTokenSource _cts = new(); @@ -2736,10 +3751,15 @@ private sealed class FakeCopilotServer : IAsyncDisposable private bool _failRuntimeShutdown; private bool _failSessionCreate; private bool _failSessionSend; + private bool _reconnectObserved; private int _nextMessageId; public bool IncludeTranscriptRecovery { get; set; } + public string? LastSessionId => _lastSessionId; public bool UniqueMessageIds { get; set; } + public bool FailSessionDelete { get; set; } + public bool FailMcpAuthInterestRegistration { get; set; } + public bool ReconnectObserved => Volatile.Read(ref _reconnectObserved); public Func? BeforeSendResponse { get; set; } @@ -2794,6 +3814,12 @@ public void ClearRequests() } } + public async Task ObserveReconnectAsync(CancellationToken cancellationToken) + { + using var connection = await _listener.AcceptTcpClientAsync(cancellationToken); + Volatile.Write(ref _reconnectObserved, true); + } + public void DelayDestroy() { _delayDestroy = true; @@ -2948,8 +3974,9 @@ private async Task RunAsync() { if (root.TryGetProperty("error", out var error)) { - completion.TrySetException(new InvalidOperationException( - error.GetProperty("message").GetString())); + var exception = new InvalidOperationException(error.GetProperty("message").GetString()); + exception.Data["error"] = error.Clone(); + completion.TrySetException(exception); } else { @@ -3006,6 +4033,20 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel }, cancellationToken); return; } + if (method == "session.eventLog.registerInterest" && FailMcpAuthInterestRegistration) + { + await WriteMessageAsync(stream, new Dictionary + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = new Dictionary + { + ["code"] = -32000, + ["message"] = "interest registration failed" + } + }, cancellationToken); + return; + } if (method == "session.send" && _failSessionSend) { _failSessionSend = false; @@ -3048,6 +4089,10 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel ["sessionId"] = paramsElement.GetProperty("sessionId").GetString(), ["sessionUri"] = "ahp-session:/" + paramsElement.GetProperty("sessionId").GetString() }, + "host.listSessions" => new Dictionary + { + ["sessions"] = Array.Empty() + }, "session.eventLog.registerInterest" => new Dictionary { ["id"] = "interest-1" @@ -3092,7 +4137,7 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel }, "session.delete" => new Dictionary { - ["success"] = true + ["success"] = !FailSessionDelete }, "session.detach" => await DetachSessionAsync(cancellationToken), "runtime.shutdown" => HandleRuntimeShutdown(), diff --git a/dotnet/test/Unit/ClientSessionSkillProviderTests.cs b/dotnet/test/Unit/ClientSessionSkillProviderTests.cs new file mode 100644 index 0000000000..c99d1f090a --- /dev/null +++ b/dotnet/test/Unit/ClientSessionSkillProviderTests.cs @@ -0,0 +1,397 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +#if NET8_0_OR_GREATER +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public sealed partial class ClientSessionLifetimeTests +{ + [Fact] + public async Task SkillProvider_Create_And_Resume_Set_Flag_Only_When_Provided() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var provider = new TestSkillProvider + { + ReadHandler = (name, _) => Task.FromResult($"# {name}") + }; + + await using var created = await client.CreateSessionAsync(new SessionConfig + { + SessionId = "skill-create", + SkillProvider = provider + }); + await using var resumed = await client.ResumeSessionAsync("skill-resume", new ResumeSessionConfig + { + SkillProvider = provider + }); + + Assert.True(Assert.Single(server.Requests, request => request.Method == "session.create") + .Params.GetProperty("hasSkillProvider").GetBoolean()); + Assert.True(Assert.Single(server.Requests, request => request.Method == "session.resume") + .Params.GetProperty("hasSkillProvider").GetBoolean()); + + var read = await server.SendRequestAsync("skillProvider.read", SkillReadRequest(resumed.SessionId, "resumed-skill")); + Assert.Equal("# resumed-skill", read.GetProperty("markdown").GetString()); + + await created.DisposeAsync(); + await resumed.DisposeAsync(); + server.ClearRequests(); + + await using var createWithoutProvider = await client.CreateSessionAsync(new SessionConfig + { + SessionId = "skill-create-no-provider" + }); + await using var resumeWithoutProvider = await client.ResumeSessionAsync("skill-resume-no-provider", new ResumeSessionConfig()); + + Assert.False(Assert.Single(server.Requests, request => request.Method == "session.create") + .Params.TryGetProperty("hasSkillProvider", out _)); + Assert.False(Assert.Single(server.Requests, request => request.Method == "session.resume") + .Params.TryGetProperty("hasSkillProvider", out _)); + } + + [Fact] + public async Task SkillProvider_Callbacks_Work_Before_Create_And_Resume_Response() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var provider = new TestSkillProvider(); + var callbackResults = new List>(); + server.BeforeResponseAsync = async (request, _) => + { + if (request.Method is "session.create" or "session.resume" + && request.Params.TryGetProperty("sessionId", out var sessionId)) + { + callbackResults.Add(await server.SendRequestWithoutWaitingAsync( + "skillProvider.list", + SkillListRequest(sessionId.GetString()!))); + } + }; + + await using var created = await client.CreateSessionAsync(new SessionConfig + { + SessionId = "pre-response-create", + SkillProvider = provider + }); + await using var resumed = await client.ResumeSessionAsync("pre-response-resume", new ResumeSessionConfig + { + SkillProvider = provider + }); + + Assert.Equal(2, callbackResults.Count); + foreach (var callback in callbackResults) + { + var result = await callback.WaitAsync(TimeSpan.FromSeconds(5)); + Assert.Equal("dynamic", Assert.Single(result.GetProperty("skills").EnumerateArray()).GetProperty("name").GetString()); + } + } + + [Fact] + public async Task SkillProvider_List_Returns_CamelCase_Descriptors_And_Omits_Null_Optionals() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var provider = new TestSkillProvider + { + ListHandler = _ => Task.FromResult>( + [ + new() + { + Name = "basic", + Description = "Basic skill" + }, + new() + { + Name = "advanced", + Description = "Advanced skill", + UserInvocable = false, + DisableModelInvocation = true, + ArgumentHint = "" + } + ]) + }; + await using var session = await client.CreateSessionAsync(new SessionConfig { SkillProvider = provider }); + + var result = await server.SendRequestAsync("skillProvider.list", SkillListRequest(session.SessionId)); + var skills = result.GetProperty("skills").EnumerateArray().ToArray(); + + Assert.Equal("basic", skills[0].GetProperty("name").GetString()); + Assert.Equal("Basic skill", skills[0].GetProperty("description").GetString()); + Assert.False(skills[0].TryGetProperty("userInvocable", out _)); + Assert.False(skills[0].TryGetProperty("disableModelInvocation", out _)); + Assert.False(skills[0].TryGetProperty("argumentHint", out _)); + Assert.False(skills[0].TryGetProperty("UserInvocable", out _)); + Assert.False(skills[0].TryGetProperty("DisableModelInvocation", out _)); + Assert.False(skills[0].TryGetProperty("ArgumentHint", out _)); + Assert.Equal("advanced", skills[1].GetProperty("name").GetString()); + Assert.False(skills[1].GetProperty("userInvocable").GetBoolean()); + Assert.True(skills[1].GetProperty("disableModelInvocation").GetBoolean()); + Assert.Equal("", skills[1].GetProperty("argumentHint").GetString()); + } + + [Fact] + public async Task SkillProvider_Null_List_Result_Is_Empty() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var provider = new TestSkillProvider + { + ListHandler = _ => Task.FromResult>(null!) + }; + await using var session = await client.CreateSessionAsync(new SessionConfig { SkillProvider = provider }); + + var result = await server.SendRequestAsync("skillProvider.list", SkillListRequest(session.SessionId)); + + Assert.Empty(result.GetProperty("skills").EnumerateArray()); + } + + [Fact] + public async Task SkillProvider_Read_Returns_Markdown_And_Null_For_Missing_Skill() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var provider = new TestSkillProvider + { + ReadHandler = (name, _) => Task.FromResult(name == "known" ? "# Known" : null) + }; + await using var session = await client.CreateSessionAsync(new SessionConfig { SkillProvider = provider }); + + var read = await server.SendRequestAsync("skillProvider.read", SkillReadRequest(session.SessionId, "known")); + + Assert.Equal("# Known", read.GetProperty("markdown").GetString()); + var missing = await server.SendRequestAsync("skillProvider.read", SkillReadRequest(session.SessionId, "missing")); + Assert.True(missing.TryGetProperty("markdown", out var markdown), missing.ToString()); + Assert.Equal(JsonValueKind.Null, markdown.ValueKind); + } + + [Fact] + public async Task SkillProvider_Provider_Failures_Do_Not_Leak_Exception_Details() + { + await using var server = await FakeCopilotServer.StartAsync(); + var logger = new SkillProviderLogger(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url), Logger = logger }); + var listFailure = new InvalidOperationException("secret list failure"); + var readFailure = new InvalidOperationException("secret read failure"); + var provider = new TestSkillProvider + { + ListHandler = _ => throw listFailure, + ReadHandler = (_, _) => throw readFailure + }; + await using var session = await client.CreateSessionAsync(new SessionConfig { SkillProvider = provider }); + + var listError = await Assert.ThrowsAsync(() => + server.SendRequestAsync("skillProvider.list", SkillListRequest(session.SessionId))); + AssertRpcError(listError, "Skill provider listSkills failed"); + + var readError = await Assert.ThrowsAsync(() => + server.SendRequestAsync("skillProvider.read", SkillReadRequest(session.SessionId, "throws"))); + AssertRpcError(readError, "Skill provider readSkill failed"); + + Assert.DoesNotContain("secret", listError.Message, StringComparison.OrdinalIgnoreCase); + Assert.DoesNotContain("secret", readError.Message, StringComparison.OrdinalIgnoreCase); + Assert.Contains(logger.Entries, entry => + entry.Message == $"Skill provider listSkills failed. SessionId={session.SessionId}" && entry.Exception == listFailure); + Assert.Contains(logger.Entries, entry => + entry.Message == $"Skill provider readSkill failed. SessionId={session.SessionId}" && entry.Exception == readFailure); + } + + [Fact] + public async Task SkillProvider_Provider_Cancellation_Without_Request_Cancellation_Is_Provider_Failure() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + var provider = new TestSkillProvider + { + ListHandler = _ => throw new TaskCanceledException("secret list timeout"), + ReadHandler = (_, _) => throw new TaskCanceledException("secret read timeout") + }; + await using var session = await client.CreateSessionAsync(new SessionConfig { SkillProvider = provider }); + + var listError = await Assert.ThrowsAsync(() => + server.SendRequestAsync("skillProvider.list", SkillListRequest(session.SessionId)).WaitAsync(TimeSpan.FromSeconds(10))); + AssertRpcError(listError, "Skill provider listSkills failed"); + + var readError = await Assert.ThrowsAsync(() => + server.SendRequestAsync("skillProvider.read", SkillReadRequest(session.SessionId, "times-out")).WaitAsync(TimeSpan.FromSeconds(10))); + AssertRpcError(readError, "Skill provider readSkill failed"); + } + + [Fact] + public async Task SkillProvider_Rejects_Unknown_NoProvider_And_Disposed_Sessions() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await client.StartAsync(); + + var unknown = await Assert.ThrowsAsync(() => + server.SendRequestAsync("skillProvider.list", SkillListRequest("unknown-session"))); + AssertRpcError(unknown, "No skill provider for session: unknown-session"); + + await using var noProvider = await client.CreateSessionAsync(new SessionConfig { SessionId = "no-provider" }); + var noProviderError = await Assert.ThrowsAsync(() => + server.SendRequestAsync("skillProvider.list", SkillListRequest(noProvider.SessionId))); + AssertRpcError(noProviderError, "No skill provider for session: no-provider"); + + var providerSession = await client.CreateSessionAsync(new SessionConfig + { + SessionId = "disposed-provider", + SkillProvider = new TestSkillProvider() + }); + await providerSession.DisposeAsync(); + var disposed = await Assert.ThrowsAsync(() => + server.SendRequestAsync("skillProvider.list", SkillListRequest("disposed-provider"))); + AssertRpcError(disposed, "No skill provider for session: disposed-provider"); + } + + [Fact] + public async Task SkillProvider_Cloud_Create_Throws_Before_Connecting() + { + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri("http://127.0.0.1:1") + }); + var provider = new TestSkillProvider(); + + var error = await Assert.ThrowsAsync(() => client.CreateSessionAsync(new SessionConfig + { + Cloud = new CloudSessionOptions + { + Repository = new CloudSessionRepository + { + Owner = "github", + Name = "copilot-sdk", + Branch = "main" + } + }, + SkillProvider = provider + })); + + Assert.Equal("Skill providers are not supported for cloud sessions.", error.Message); + Assert.Equal(0, provider.ListCalls); + Assert.Equal(0, provider.ReadCalls); + } + + [Fact] + public async Task SkillProvider_EmptyMode_Defaults_EnableSkills_To_False() + { + await using var server = await FakeCopilotServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url), + Mode = CopilotClientMode.Empty, + BaseDirectory = Path.GetTempPath(), + }); + + await using var session = await client.CreateSessionAsync(new SessionConfig + { + AvailableTools = [], + SkillProvider = new TestSkillProvider() + }); + + var request = Assert.Single(server.Requests, request => request.Method == "session.create"); + Assert.True(request.Params.GetProperty("hasSkillProvider").GetBoolean()); + Assert.False(request.Params.GetProperty("enableSkills").GetBoolean()); + } + + private static Dictionary SkillListRequest(string sessionId) => new() + { + ["sessionId"] = sessionId + }; + + private static Dictionary SkillReadRequest(string sessionId, string name) => new() + { + ["sessionId"] = sessionId, + ["name"] = name + }; + + private static void AssertRpcError(InvalidOperationException exception, string expectedMessage) + { + Assert.Equal(expectedMessage, exception.Message); + var error = Assert.IsType(exception.Data["error"]); + Assert.Equal(-32603, error.GetProperty("code").GetInt32()); + Assert.Equal(expectedMessage, error.GetProperty("message").GetString()); + Assert.False(error.TryGetProperty("data", out _), error.ToString()); + } + + private sealed class SkillProviderLogger : ILogger + { + private readonly ConcurrentQueue<(string Message, Exception? Exception)> _entries = new(); + + public IReadOnlyCollection<(string Message, Exception? Exception)> Entries => _entries; + + public IDisposable? BeginScope(TState state) where TState : notnull => null; + + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => + _entries.Enqueue((formatter(state, exception), exception)); + } + + private sealed class TestSkillProvider : ISkillProvider + { + public int ListCalls; + public int ReadCalls; + + public Func>> ListHandler { get; init; } = + _ => Task.FromResult>( + [ + new() + { + Name = "dynamic", + Description = "Dynamic skill" + } + ]); + + public Func> ReadHandler { get; init; } = + (name, _) => Task.FromResult($"# {name}"); + + public Task> ListSkillsAsync(CancellationToken cancellationToken) + { + Interlocked.Increment(ref ListCalls); + return ListHandler(cancellationToken); + } + + public Task ReadSkillAsync(string name, CancellationToken cancellationToken) + { + Interlocked.Increment(ref ReadCalls); + return ReadHandler(name, cancellationToken); + } + } + + private sealed partial class FakeCopilotServer + { + public async Task> SendRequestWithoutWaitingAsync(string method, Dictionary parameters) + { + var stream = _stream ?? throw new InvalidOperationException("Client is not connected."); + var id = Interlocked.Increment(ref _nextRequestId); + var completion = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + if (!_pendingRequests.TryAdd(id, completion)) + { + throw new InvalidOperationException("Failed to track callback request."); + } + + await WriteMessageAsync(stream, new Dictionary + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["method"] = method, + ["params"] = parameters + }, _cts.Token); + + return completion.Task.WaitAsync(_cts.Token); + } + } +} +#endif diff --git a/dotnet/test/Unit/CloneTests.cs b/dotnet/test/Unit/CloneTests.cs index c67bce8b4a..3ae4c0b009 100644 --- a/dotnet/test/Unit/CloneTests.cs +++ b/dotnet/test/Unit/CloneTests.cs @@ -88,6 +88,15 @@ private sealed class TestExtensionLaunchProvider : GitHub.Copilot.Rpc.IExtension Task.FromResult(new GitHub.Copilot.Rpc.ExtensionLaunchProviderResolveResult()); } + private sealed class TestSkillProvider : ISkillProvider + { + public Task> ListSkillsAsync(CancellationToken cancellationToken) => + Task.FromResult>([]); + + public Task ReadSkillAsync(string name, CancellationToken cancellationToken) => + Task.FromResult(null); + } + [Fact] public void SessionConfig_Clone_CopiesAllProperties() { @@ -205,6 +214,28 @@ public void SessionConfig_RefreshCustomInstructions_IsCreateOnlyAndDefaultsToNul Assert.Null(typeof(ResumeSessionConfig).GetProperty(nameof(SessionConfig.RefreshCustomInstructions))); } + [Fact] + public void SessionConfig_Clone_CopiesSkillProvider() + { + var provider = new TestSkillProvider(); + var original = new SessionConfig { SkillProvider = provider }; + + var clone = original.Clone(); + + Assert.Same(provider, clone.SkillProvider); + } + + [Fact] + public void ResumeSessionConfig_Clone_CopiesSkillProvider() + { + var provider = new TestSkillProvider(); + var original = new ResumeSessionConfig { SkillProvider = provider }; + + var clone = original.Clone(); + + Assert.Same(provider, clone.SkillProvider); + } + [Fact] public void SessionConfig_Clone_CollectionsAreIndependent() { diff --git a/dotnet/test/Unit/E2ETestFixtureTests.cs b/dotnet/test/Unit/E2ETestFixtureTests.cs index e2d7cd0723..933b82c1fc 100644 --- a/dotnet/test/Unit/E2ETestFixtureTests.cs +++ b/dotnet/test/Unit/E2ETestFixtureTests.cs @@ -142,4 +142,38 @@ public async Task Replay_Proxy_Starts_With_Connect_Metadata_And_Stops() await proxy.StopAsync(skipWritingCache: true); } } + + [Theory] + [InlineData(false, null)] + [InlineData(true, 0)] + [InlineData(true, 1)] + public void Proxy_Startup_Diagnostics_Preserve_Process_State_And_Bounded_Output(bool largeOutput, int? exitCode) + { + var output = new ReplayProxy.StartupDiagnostics(); + output.AppendOutput("old stdout"); + output.AppendError("old stderr"); + if (largeOutput) + { + output.AppendOutput(new string('x', 12_000)); + output.AppendError(new string('y', 12_000)); + } + output.AppendOutput("tsx server.ts"); + output.AppendError("proxy startup error"); + + var diagnostics = output.Describe(1234, TimeSpan.FromSeconds(30), exitCode); + + Assert.Contains("PID 1234", diagnostics); + Assert.Contains(exitCode is int code ? $"exited with code {code}" : "running", diagnostics); + Assert.Contains("--- stdout tail ---", diagnostics); + Assert.Contains("tsx server.ts", diagnostics); + Assert.Contains("--- stderr tail ---", diagnostics); + Assert.Contains("proxy startup error", diagnostics); + if (largeOutput) + { + Assert.DoesNotContain("old stdout", diagnostics); + Assert.DoesNotContain("old stderr", diagnostics); + } + Assert.True(diagnostics.Length < 8500, $"Diagnostic length: {diagnostics.Length}"); + } + } diff --git a/dotnet/test/Unit/ExtensionLaunchDiagnosticsTests.cs b/dotnet/test/Unit/ExtensionLaunchDiagnosticsTests.cs index af70515c9f..a13931b42b 100644 --- a/dotnet/test/Unit/ExtensionLaunchDiagnosticsTests.cs +++ b/dotnet/test/Unit/ExtensionLaunchDiagnosticsTests.cs @@ -9,6 +9,44 @@ namespace GitHub.Copilot.Test.Unit; public class ExtensionLaunchDiagnosticsTests { + [Fact] + public void Reports_Recent_Error_Categories_Without_Exposing_Tail_Lines() + { + var homeDir = Path.Join(Path.GetTempPath(), $"extension-diagnostics-{Guid.NewGuid():N}"); + var logsDir = Path.Join(homeDir, "logs"); + Directory.CreateDirectory(logsDir); + try + { + File.WriteAllLines(Path.Join(logsDir, "process-1.log"), + [ + "=== module=/private/project/sample-extension/extension.mjs ===", + "TypeError: old failure", + .. Enumerable.Range(0, 20).Select(index => $"context {index}"), + "Error: connect ECONNREFUSED to secret-host.example using secret-user-input", + "=== secret-user-input ===", + "=== exit code=123456789 disposition=crash ===", + "[sdk-extension-test] join failed code=123456789", + "Error [ERR_SECRET_USER_INPUT]: secret-user-input", + ]); + + var summary = TestHelper.ExtensionLaunchMarkers(homeDir, "user:sample-extension"); + + Assert.Contains("recent error categories: ECONNREFUSED, Error", summary); + Assert.DoesNotContain("TypeError", summary.Split(["recent error categories:"], StringSplitOptions.None)[1]); + Assert.DoesNotContain("secret-user-input", summary); + Assert.DoesNotContain("ERR_SECRET_USER_INPUT", summary); + Assert.Contains("=== exit code=other disposition=crash ===", summary); + Assert.Contains("[sdk-extension-test] join failed", summary); + Assert.DoesNotContain("123456789", summary); + Assert.DoesNotContain("secret-host.example", summary); + Assert.DoesNotContain("/private/project", summary); + } + finally + { + Directory.Delete(homeDir, recursive: true); + } + } + [Fact] public void Reports_Exception_Categories_Without_Exposing_Child_Stderr() { @@ -25,6 +63,12 @@ public void Reports_Exception_Categories_Without_Exposing_Child_Stderr() Error: connect ECONNRESET to private-host.example === exit code=1 disposition=crash === """); + File.WriteAllText(Path.Join(logsDir, "process-2.log"), """ + === module=/private/project/other-extension/extension.mjs === + ReferenceError: unrelated-child-error + """); + using var activeWriter = new FileStream( + Path.Join(logsDir, "process-1.log"), FileMode.Open, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete); var summary = TestHelper.ExtensionLaunchMarkers(homeDir, "user:sample-extension"); @@ -38,6 +82,7 @@ public void Reports_Exception_Categories_Without_Exposing_Child_Stderr() Assert.DoesNotContain("secret-user-input", summary); Assert.DoesNotContain("private-host.example", summary); Assert.DoesNotContain("/private/project", summary); + Assert.DoesNotContain("ReferenceError", summary); } finally { diff --git a/dotnet/test/Unit/JsonRpcTests.cs b/dotnet/test/Unit/JsonRpcTests.cs index 71189e4c59..9595d8bd22 100644 --- a/dotnet/test/Unit/JsonRpcTests.cs +++ b/dotnet/test/Unit/JsonRpcTests.cs @@ -102,12 +102,15 @@ public async Task JsonRpc_Cancels_Incoming_Handler_Token_When_Connection_Dispose using var pair = JsonRpcReflectionPair.Create(); var handlerStarted = new TaskCompletionSource( TaskCreationOptions.RunContinuationsAsynchronously); + var handlerDispatched = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); pair.Server.SetLocalRpcMethod("waitForShutdown", (Func)(async cancellationToken => { + await handlerDispatched.Task; handlerStarted.SetResult(cancellationToken); await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken); })); + pair.Server.SetLocalRpcMethod("handlerDispatched", (Action)(() => handlerDispatched.SetResult())); if (requestHasId) { @@ -117,6 +120,8 @@ public async Task JsonRpc_Cancels_Incoming_Handler_Token_When_Connection_Dispose { await pair.SendClientFrameAsync(CreateNotificationFrame("waitForShutdown", "{}")); } + // Keep the handler suspended until dispatch returns, before testing shutdown cancellation. + await pair.SendClientFrameAsync(CreateNotificationFrame("handlerDispatched", "{}")); var handlerToken = await WaitForCancellationAsync(handlerStarted.Task); Assert.False(handlerToken.IsCancellationRequested); @@ -126,6 +131,24 @@ public async Task JsonRpc_Cancels_Incoming_Handler_Token_When_Connection_Dispose await WaitForCancellationAsync(handlerToken); } + [Fact] + public async Task JsonRpc_Invokes_Task_Returning_Handler_To_Completion() + { + using var pair = JsonRpcReflectionPair.Create(); + pair.StartListening(); + + var observed = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + pair.Server.SetLocalRpcMethod("voidAsync", (Func)(async (name, count, cancellationToken) => + { + await Task.Yield(); + observed.SetResult($"{name}:{count}"); + })); + + await pair.Client.InvokeAsync("voidAsync", ["Mona", 2]).WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.Equal("Mona:2", await observed.Task.WaitAsync(TimeSpan.FromSeconds(5))); + } + [Fact] public async Task JsonRpc_Awaits_Declared_Task_Handler_With_Generic_Runtime_Task() { @@ -258,6 +281,15 @@ public async Task JsonRpc_Malformed_Session_Event_Does_Not_Block_Unknown_Or_Know var malformed = CreateNotificationFrame( "session.event", """{"sessionId":"session-1","event":42}"""); + var malformedDiscriminator = CreateNotificationFrame( + "session.event", + """{"sessionId":"session-1","event":{"type":false}}"""); + var duplicateDiscriminator = CreateNotificationFrame( + "session.event", + """{"sessionId":"session-1","event":{"type":"session.idle","data":{},"type":"session.idle"}}"""); + var unexpectedMetadata = CreateNotificationFrame( + "session.event", + """{"sessionId":"session-1","event":{"type":"future.event","$id":"1"}}"""); var unknown = CreateNotificationFrame( "session.event", """ @@ -291,7 +323,8 @@ public async Task JsonRpc_Malformed_Session_Event_Does_Not_Block_Unknown_Or_Know } """); - using var receiveStream = new MemoryStream(CombineFrames([malformed, unknown, known])); + using var receiveStream = new MemoryStream(CombineFrames( + [malformed, malformedDiscriminator, duplicateDiscriminator, unexpectedMetadata, unknown, known])); using var rpc = new JsonRpcReflection(Stream.Null, receiveStream); var collector = new SessionEventCollector(expectedCount: 2); rpc.SetLocalRpcMethod("session.event", (Action)collector.Handle); @@ -455,11 +488,17 @@ private sealed class JsonRpcReflection : IDisposable private static readonly Type JsonRpcType = typeof(CopilotClient).Assembly.GetType("GitHub.Copilot.JsonRpc", throwOnError: true)!; - private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web) + private static readonly JsonSerializerOptions SerializerOptions = CreateSerializerOptions(); + + private static JsonSerializerOptions CreateSerializerOptions() { - AllowOutOfOrderMetadataProperties = true, - TypeInfoResolver = new DefaultJsonTypeInfoResolver(), - }; + var property = typeof(CopilotClient).GetProperty( + "SerializerOptionsForMessageFormatter", + BindingFlags.Static | BindingFlags.NonPublic)!; + var options = new JsonSerializerOptions((JsonSerializerOptions)property.GetValue(null)!); + options.TypeInfoResolverChain.Add(new DefaultJsonTypeInfoResolver()); + return options; + } private readonly object _instance; diff --git a/dotnet/test/Unit/MSBuildTargetsTests.cs b/dotnet/test/Unit/MSBuildTargetsTests.cs index 6a26a95688..2e855a29be 100644 --- a/dotnet/test/Unit/MSBuildTargetsTests.cs +++ b/dotnet/test/Unit/MSBuildTargetsTests.cs @@ -245,6 +245,7 @@ public async Task RuntimePackageAssets_AreFilteredAndCopiedToOutput() sandbox.WriteRuntimeCacheAsset("preloads", "extension_bootstrap.mjs", "preload"); sandbox.WriteRuntimeCacheAsset("sdk", "factory.js", "factory"); sandbox.WriteRuntimeCacheAsset("app.js", "excluded"); + sandbox.WriteRuntimeCacheAsset("cli-main.js", "excluded"); sandbox.WriteRuntimeCacheAsset("LICENSE.md", "excluded"); sandbox.WriteRuntimeCacheAsset("README.md", "excluded"); sandbox.WriteStaleOutputRuntimeAsset("obsolete", "tool", "stale"); @@ -261,6 +262,7 @@ public async Task RuntimePackageAssets_AreFilteredAndCopiedToOutput() Assert.Equal("preload", File.ReadAllText(sandbox.ExpectedRuntimeAsset("preloads", "extension_bootstrap.mjs"))); Assert.Equal("factory", File.ReadAllText(sandbox.ExpectedRuntimeAsset("sdk", "factory.js"))); Assert.False(File.Exists(sandbox.ExpectedRuntimeAsset("app.js"))); + Assert.False(File.Exists(sandbox.ExpectedRuntimeAsset("cli-main.js"))); Assert.False(File.Exists(sandbox.ExpectedRuntimeAsset("LICENSE.md"))); Assert.False(File.Exists(sandbox.ExpectedRuntimeAsset("README.md"))); Assert.False(File.Exists(sandbox.ExpectedRuntimeAsset("obsolete", "tool"))); @@ -274,6 +276,8 @@ public async Task PackAsTool_NoBuild_IncludesRuntimeAssetsInToolPackage() sandbox.WriteRuntimeCacheAsset("prebuilds", GetReleasePlatform(), "runtime.node", "runtime"); sandbox.WriteRuntimeCacheAsset("prebuilds", GetReleasePlatform(), RuntimeWrapperName, "wrapper"); sandbox.WriteRuntimeCacheAsset("ripgrep", "bin", GetReleasePlatform(), "rg", "ripgrep"); + sandbox.WriteRuntimeCacheAsset("app.js", "excluded"); + sandbox.WriteRuntimeCacheAsset("cli-main.js", "excluded"); var result = await sandbox.PackNoBuildAsync(new Dictionary { @@ -290,6 +294,8 @@ public async Task PackAsTool_NoBuild_IncludesRuntimeAssetsInToolPackage() Assert.Contains($"{nativePath}/runtime.node", entries); Assert.Contains($"{nativePath}/{RuntimeLibraryName}", entries); Assert.Contains($"{nativePath}/ripgrep/bin/{GetReleasePlatform()}/rg", entries); + Assert.DoesNotContain($"{nativePath}/app.js", entries); + Assert.DoesNotContain($"{nativePath}/cli-main.js", entries); } [Fact] @@ -444,6 +450,11 @@ public static MSBuildSandbox Create(bool packAsTool = false, string version = "0 """; File.WriteAllText(Path.Combine(dir, "App.csproj"), csproj); + File.WriteAllText(Path.Combine(dir, "NuGet.Config"), + ""); + // Match the SDK used by setup restore, which provisioned the offline framework packs. + File.Copy(Path.Combine(Path.GetDirectoryName(TargetsFilePath)!, "..", "..", "global.json"), + Path.Combine(dir, "global.json")); File.WriteAllText( Path.Combine(dir, "Stub.cs"), packAsTool diff --git a/dotnet/test/Unit/McpPromptsTests.cs b/dotnet/test/Unit/McpPromptsTests.cs new file mode 100644 index 0000000000..918680594c --- /dev/null +++ b/dotnet/test/Unit/McpPromptsTests.cs @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using GitHub.Copilot.Rpc; +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +/// Generated result fidelity complements the real MCP transport E2E. +public class McpPromptsTests +{ + private static readonly JsonSerializerOptions Options = new() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + TypeInfoResolver = new DefaultJsonTypeInfoResolver(), + }; + + [Theory] + [InlineData("firstPage")] + [InlineData("secondPage")] + [InlineData("richPrompt")] + public void Results_Preserve_Optional_Fields_And_Opaque_Content(string fixtureName) + { + var directory = new DirectoryInfo(AppContext.BaseDirectory); + string? fixturePath = null; + while (directory is not null) + { + var candidate = Path.Combine(directory.FullName, "test", "harness", "mcp-prompt-fixtures.json"); + if (File.Exists(candidate)) + { + fixturePath = candidate; + break; + } + directory = directory.Parent; + } + Assert.NotNull(fixturePath); + var expected = JsonNode.Parse(File.ReadAllText(fixturePath))![fixtureName]!; + var actual = fixtureName == "richPrompt" + ? JsonSerializer.SerializeToNode(expected.Deserialize(Options), Options) + : JsonSerializer.SerializeToNode(expected.Deserialize(Options), Options); + Assert.True(JsonNode.DeepEquals(expected, actual), $"Expected: {expected}\nActual: {actual}"); + } +} diff --git a/dotnet/test/Unit/ReplayProxyTests.cs b/dotnet/test/Unit/ReplayProxyTests.cs new file mode 100644 index 0000000000..827d7103d7 --- /dev/null +++ b/dotnet/test/Unit/ReplayProxyTests.cs @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +using System.Text.Json; +using System.Text.Json.Serialization; +using GitHub.Copilot.Test.Harness; +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public partial class ReplayProxyTests +{ + [Theory] + [InlineData("37", 37L)] + [InlineData("0", 0L)] + [InlineData("", null)] + [InlineData("null", null)] + public void CapturedCompactionUsagePreservesInputCounts(string inputTokens, long? expectedInputTokens) + { + var inputProperty = inputTokens.Length == 0 ? string.Empty : ",\"inputTokens\":" + inputTokens; + var json = "[{\"request\":{\"model\":\"test-model\",\"messages\":[]}," + + "\"response\":{\"id\":\"compaction-response\",\"model\":\"test-model\"," + + "\"choices\":[]}," + + "\"compactionUsage\":{\"interactionId\":\"compaction-interaction\"," + + "\"summary\":\"summary\",\"responseCount\":2" + inputProperty + "}," + + "\"requestHeaders\":{\"x-interaction-type\":\"conversation-compaction\"}}]"; + var exchanges = JsonSerializer.Deserialize(json, ReplayProxyTestJsonContext.Default.ListParsedHttpExchange); + Assert.NotNull(exchanges); + var exchange = Assert.Single(exchanges); + Assert.NotNull(exchange.RequestHeaders); + Assert.Equal("conversation-compaction", exchange.RequestHeaders["x-interaction-type"].GetString()); + Assert.NotNull(exchange.CompactionUsage); + Assert.Equal("compaction-interaction", exchange.CompactionUsage.InteractionId); + Assert.Equal("summary", exchange.CompactionUsage.Summary); + Assert.Equal(2, exchange.CompactionUsage.ResponseCount); + Assert.Equal(expectedInputTokens, exchange.CompactionUsage.InputTokens); + } + + [JsonSourceGenerationOptions(JsonSerializerDefaults.Web)] + [JsonSerializable(typeof(List))] + private partial class ReplayProxyTestJsonContext : JsonSerializerContext; +} diff --git a/dotnet/test/Unit/RpcMcpListTests.cs b/dotnet/test/Unit/RpcMcpListTests.cs new file mode 100644 index 0000000000..a662d49212 --- /dev/null +++ b/dotnet/test/Unit/RpcMcpListTests.cs @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +#if NET8_0_OR_GREATER +using System.Text.Json; +using GitHub.Copilot.Rpc; +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public sealed partial class ClientSessionLifetimeTests +{ + private static readonly string[] ExpectedMcpLifecycleStatuses = ["not_configured", "connected", "needs-auth", "stopped", "failed"]; + + [Fact] + public async Task Session_Rpc_Mcp_ListConfigured_Preserves_Runtime_Lifecycle() + { + await using var server = await FakeCopilotServer.StartAsync(); + using var response = JsonDocument.Parse(""" + { + "servers": [ + { "name": "cold", "enabled": true, "live": { "status": "not_configured" } }, + { "name": "old-connection", "enabled": false, "live": { "status": "connected" } }, + { "name": "auth", "enabled": true, "live": { "status": "needs-auth" } }, + { "name": "stopped", "enabled": true, "live": { "status": "stopped" } }, + { "name": "failed", "enabled": true, "live": { "status": "failed", "error": "connection failed" } } + ] + } + """); + server.ResponseFactory = _ => response.RootElement; + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url) + }); + await using var session = await client.CreateSessionAsync(new SessionConfig()); + server.ClearRequests(); + + var inventory = await session.Rpc.Mcp.ListConfiguredAsync(); + Assert.Equal( + ExpectedMcpLifecycleStatuses, + inventory.Servers.Select(entry => entry.Live.Status.Value)); + Assert.False(inventory.Servers[1].Enabled); + Assert.Equal("connection failed", inventory.Servers[4].Live.Error); + AssertConnectorRequest(Assert.Single(server.Requests), "session.mcp.listConfigured", session.SessionId); + } + + [Fact] + public async Task Session_Rpc_Mcp_List_And_ListConfigured_Use_Parameterless_Wire_Contracts() + { + await using var server = await FakeCopilotServer.StartAsync(); + server.ResponseFactory = _ => new Dictionary { ["servers"] = Array.Empty() }; + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url) + }); + await using var session = await client.CreateSessionAsync(new SessionConfig()); + server.ClearRequests(); + + var mcp = session.Rpc.Mcp; + Func> legacyList = mcp.ListAsync; + Func> configuredList = mcp.ListConfiguredAsync; + Assert.NotNull(mcp.GetType().GetMethod("ListAsync", [typeof(CancellationToken)])); + Assert.NotNull(mcp.GetType().GetMethod("ListConfiguredAsync", [typeof(CancellationToken)])); + await mcp.ListAsync(); + await legacyList(CancellationToken.None); + await mcp.ListConfiguredAsync(); + await configuredList(CancellationToken.None); + + Assert.Collection( + server.Requests, + request => AssertConnectorRequest(request, "session.mcp.list", session.SessionId), + request => AssertConnectorRequest(request, "session.mcp.list", session.SessionId), + request => AssertConnectorRequest(request, "session.mcp.listConfigured", session.SessionId), + request => AssertConnectorRequest(request, "session.mcp.listConfigured", session.SessionId)); + } +} +#endif diff --git a/dotnet/test/Unit/RpcMcpPromptsTests.cs b/dotnet/test/Unit/RpcMcpPromptsTests.cs new file mode 100644 index 0000000000..ae18b70c27 --- /dev/null +++ b/dotnet/test/Unit/RpcMcpPromptsTests.cs @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +#if NET8_0_OR_GREATER +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public sealed partial class ClientSessionLifetimeTests +{ + [Fact] + public async Task Mcp_Prompts_Bind_Session_And_Preserve_Omitted_And_Empty_Arguments() + { + // TypeScript owns runtime behavior; this fixture checks .NET wire encoding. + await using var server = await FakeCopilotServer.StartAsync(); + server.ResponseFactory = request => request.Method switch + { + "session.mcp.prompts.list" => new Dictionary + { + ["prompts"] = Array.Empty(), + }, + "session.mcp.prompts.get" => new Dictionary + { + ["messages"] = Array.Empty(), + }, + _ => throw new InvalidOperationException($"Unexpected prompt request '{request.Method}'."), + }; + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url) + }); + await using var session = await client.CreateSessionAsync(new SessionConfig()); + server.ClearRequests(); + + Assert.Empty((await session.Rpc.Mcp.Prompts.ListAsync("fixture")).Prompts); + await session.Rpc.Mcp.Prompts.ListAsync("fixture", "page:2/opaque+cursor="); + Assert.Empty((await session.Rpc.Mcp.Prompts.GetAsync("fixture", "echo")).Messages); + await session.Rpc.Mcp.Prompts.GetAsync("fixture", "echo", new Dictionary()); + await session.Rpc.Mcp.Prompts.GetAsync("fixture", "echo", + new Dictionary { ["topic"] = "日本語", ["style"] = "" }); + + var requests = server.Requests.ToArray(); + Assert.Equal(5, requests.Length); + for (var i = 0; i < requests.Length; i++) + { + var request = requests[i]; + Assert.Equal(i < 2 ? "session.mcp.prompts.list" : "session.mcp.prompts.get", request.Method); + Assert.Equal(session.SessionId, request.Params.GetProperty("sessionId").GetString()); + Assert.Equal("fixture", request.Params.GetProperty("serverName").GetString()); + if (i >= 2) + { + Assert.Equal("echo", request.Params.GetProperty("promptName").GetString()); + } + } + Assert.False(requests[0].Params.TryGetProperty("cursor", out _)); + Assert.Equal("page:2/opaque+cursor=", requests[1].Params.GetProperty("cursor").GetString()); + Assert.False(requests[2].Params.TryGetProperty("arguments", out _)); + Assert.Empty(requests[3].Params.GetProperty("arguments").EnumerateObject()); + var arguments = requests[4].Params.GetProperty("arguments"); + Assert.Equal("日本語", arguments.GetProperty("topic").GetString()); + Assert.Equal("", arguments.GetProperty("style").GetString()); + } +} +#endif diff --git a/dotnet/test/Unit/RpcModelCompatibilityTests.cs b/dotnet/test/Unit/RpcModelCompatibilityTests.cs new file mode 100644 index 0000000000..d638c86d35 --- /dev/null +++ b/dotnet/test/Unit/RpcModelCompatibilityTests.cs @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * 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 void Existing_Model_Switch_Signature_And_Defaults_Are_Preserved() + { + Type[] types = + [ + typeof(string), typeof(AutoTier?), typeof(string), typeof(ReasoningSummary?), + typeof(Verbosity?), typeof(ModelCapabilitiesOverride), typeof(ContextTier?), + typeof(ModelChangeSource?), typeof(bool?), typeof(string), typeof(bool?), + typeof(string), typeof(string), typeof(bool?), typeof(ModelPickerPersistenceRequest), + typeof(CancellationToken), + ]; + var method = typeof(ModelApi).GetMethod("SwitchToAsync", types); + Assert.NotNull(method); + var parameters = method.GetParameters(); + Assert.False(parameters[0].IsOptional); + Assert.Equal("cancellationToken", parameters[^1].Name); + Assert.All(parameters.Skip(1), parameter => + { + Assert.True(parameter.IsOptional); + Assert.True(parameter.HasDefaultValue); + Assert.Null(parameter.DefaultValue); + }); + } + + [Fact] + public async Task Existing_Positional_Model_Switch_Calls_Preserve_Wire_Values() + { + 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.CreateSessionAsync(new SessionConfig()); + server.ClearRequests(); + + await session.Rpc.Model.SwitchToAsync("auto", AutoTier.Balance); + await session.Rpc.Model.SwitchToAsync( + "auto", AutoTier.Efficiency, "high", null, null, null, null, null, + true, "compact", true, "repo", "global", true, null, CancellationToken.None); + + var requests = server.Requests.ToArray(); + Assert.Equal(2, requests.Length); + foreach (var request in requests) + { + Assert.Equal("session.model.switchTo", request.Method); + Assert.Equal(session.SessionId, request.Params.GetProperty("sessionId").GetString()); + Assert.Equal("auto", request.Params.GetProperty("modelId").GetString()); + Assert.False(request.Params.TryGetProperty("providerId", out _)); + } + Assert.Equal("balance", requests[0].Params.GetProperty("autoTier").GetString()); + Assert.False(requests[0].Params.TryGetProperty("reasoningEffort", out _)); + Assert.Equal("efficiency", requests[1].Params.GetProperty("autoTier").GetString()); + Assert.Equal("high", requests[1].Params.GetProperty("reasoningEffort").GetString()); + Assert.True(requests[1].Params.GetProperty("deferIfModelChangeQueued").GetBoolean()); + Assert.Equal("compact", requests[1].Params.GetProperty("compactionDecision").GetString()); + Assert.True(requests[1].Params.GetProperty("runCompactionPreflight").GetBoolean()); + Assert.Equal("repo", requests[1].Params.GetProperty("repoScope").GetString()); + Assert.Equal("global", requests[1].Params.GetProperty("modelChangeScope").GetString()); + Assert.True(requests[1].Params.GetProperty("requireAvailable").GetBoolean()); + } + + [Fact] + public async Task Provider_Qualified_Model_Switch_Uses_Request_With_Sdk_Owned_Session() + { + 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.CreateSessionAsync(new SessionConfig()); + server.ClearRequests(); + + var result = await session.Rpc.Model.SwitchToAsync(new ModelSwitchToRequest + { + ModelId = "shared-model", + ProviderId = "provider-a", + ReasoningEffort = "high", + }, CancellationToken.None); + + Assert.Null(typeof(ModelSwitchToRequest).GetProperty("SessionId")); + var request = Assert.Single(server.Requests); + Assert.Equal("session.model.switchTo", request.Method); + Assert.Equal(session.SessionId, request.Params.GetProperty("sessionId").GetString()); + Assert.Equal("shared-model", request.Params.GetProperty("modelId").GetString()); + Assert.Equal("provider-a", request.Params.GetProperty("providerId").GetString()); + Assert.Equal("high", request.Params.GetProperty("reasoningEffort").GetString()); + Assert.False(request.Params.TryGetProperty("autoTier", out _)); + // The fake reports "auto"; the SDK must return the server's result, not the requested id. + Assert.Equal("auto", result.ModelId); + } +} +#endif diff --git a/dotnet/test/Unit/RpcWorkspacesCompatibilityTests.cs b/dotnet/test/Unit/RpcWorkspacesCompatibilityTests.cs new file mode 100644 index 0000000000..8653a6b09a --- /dev/null +++ b/dotnet/test/Unit/RpcWorkspacesCompatibilityTests.cs @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- + * 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 void Workspace_Diff_Preserves_Existing_Signature_And_Defaults() + { + var method = typeof(WorkspacesApi).GetMethod("DiffAsync", + [typeof(WorkspaceDiffMode), typeof(bool?), typeof(CancellationToken)]); + Assert.NotNull(method); + Assert.Equal( + new (string? Name, bool IsOptional)[] + { + ("mode", false), + ("ignoreWhitespace", true), + ("cancellationToken", true), + }, + method.GetParameters().Select(parameter => (parameter.Name, parameter.IsOptional))); + Assert.All(method.GetParameters().Skip(1), parameter => Assert.Null(parameter.DefaultValue)); + } + + [Fact] + public async Task Workspace_Diff_Preserves_Positional_Calls_And_Offers_Contents_Through_Request() + { + await using var server = await FakeCopilotServer.StartAsync(); + server.ResponseFactory = request => request.Method == "session.workspaces.diff" + ? new Dictionary + { + ["mode"] = "session", + ["requestedMode"] = "session", + ["isFallback"] = false, + ["changes"] = Array.Empty(), + } + : throw new InvalidOperationException($"Unexpected workspace diff request '{request.Method}'."); + await using var client = new CopilotClient(new CopilotClientOptions + { + Connection = RuntimeConnection.ForUri(server.Url) + }); + await using var session = await client.CreateSessionAsync(new SessionConfig()); + server.ClearRequests(); + var cancellation = CancellationToken.None; + + await session.Rpc.Workspaces.DiffAsync(WorkspaceDiffMode.Session, false, cancellation); + await session.Rpc.Workspaces.DiffAsync(WorkspaceDiffMode.Session); + await session.Rpc.Workspaces.DiffAsync(WorkspaceDiffMode.Session, cancellationToken: cancellation); + await session.Rpc.Workspaces.DiffAsync(new WorkspacesDiffRequest + { + Mode = WorkspaceDiffMode.Session, + IgnoreWhitespace = false, + IncludeContents = true, + }, cancellation); + await session.Rpc.Workspaces.DiffAsync(new WorkspacesDiffRequest + { + Mode = WorkspaceDiffMode.Session, + IncludeContents = false, + }, cancellation); + + Assert.Null(typeof(WorkspacesDiffRequest).GetProperty("SessionId")); + var requests = server.Requests.ToArray(); + Assert.All(requests, request => Assert.Equal( + ("session.workspaces.diff", session.SessionId, "session"), + (request.Method, request.Params.GetProperty("sessionId").GetString(), + request.Params.GetProperty("mode").GetString()))); + Assert.Equal( + new (bool? IgnoreWhitespace, bool? IncludeContents)[] + { + (false, null), + (null, null), + (null, null), + (false, true), + (null, false), + }, + requests.Select(request => ( + request.Params.TryGetProperty("ignoreWhitespace", out var ignoreWhitespace) + ? ignoreWhitespace.GetBoolean() : (bool?)null, + request.Params.TryGetProperty("includeContents", out var includeContents) + ? includeContents.GetBoolean() : (bool?)null))); + } +} +#endif diff --git a/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs index 09f212cd7e..ef41cfa4aa 100644 --- a/dotnet/test/Unit/SerializationTests.cs +++ b/dotnet/test/Unit/SerializationTests.cs @@ -106,6 +106,7 @@ public void ProviderConfig_CanSerializeHeaders_WithSdkOptions() Headers = new Dictionary { ["Authorization"] = "Bearer provider-token" }, ModelId = "gpt-4o", WireModel = "my-finetune-v3", + ModelProvider = "lm_studio", MaxPromptTokens = 100_000, MaxOutputTokens = 4096, Transport = "websockets" @@ -118,6 +119,7 @@ public void ProviderConfig_CanSerializeHeaders_WithSdkOptions() Assert.Equal("Bearer provider-token", root.GetProperty("headers").GetProperty("Authorization").GetString()); Assert.Equal("gpt-4o", root.GetProperty("modelId").GetString()); Assert.Equal("my-finetune-v3", root.GetProperty("wireModel").GetString()); + Assert.Equal("lm_studio", root.GetProperty("modelProvider").GetString()); Assert.Equal(100_000, root.GetProperty("maxPromptTokens").GetInt32()); Assert.Equal(4096, root.GetProperty("maxOutputTokens").GetInt32()); Assert.Equal("websockets", root.GetProperty("transport").GetString()); @@ -128,11 +130,35 @@ public void ProviderConfig_CanSerializeHeaders_WithSdkOptions() Assert.Equal("Bearer provider-token", deserialized.Headers!["Authorization"]); Assert.Equal("gpt-4o", deserialized.ModelId); Assert.Equal("my-finetune-v3", deserialized.WireModel); + Assert.Equal("lm_studio", deserialized.ModelProvider); Assert.Equal(100_000, deserialized.MaxPromptTokens); Assert.Equal(4096, deserialized.MaxOutputTokens); Assert.Equal("websockets", deserialized.Transport); } + [Fact] + public void NamedProviderConfig_CanSerializeModelProvider_WithSdkOptions() + { + var options = GetSerializerOptions(); + var original = new NamedProviderConfig + { + Name = "local", + Type = "openai", + BaseUrl = "http://localhost:11434/v1", + ModelProvider = "ollama" + }; + + var json = JsonSerializer.Serialize(original, options); + using var document = JsonDocument.Parse(json); + var root = document.RootElement; + Assert.Equal("local", root.GetProperty("name").GetString()); + Assert.Equal("ollama", root.GetProperty("modelProvider").GetString()); + + var deserialized = JsonSerializer.Deserialize(json, options); + Assert.NotNull(deserialized); + Assert.Equal("ollama", deserialized.ModelProvider); + } + [Fact] public void CapiSessionOptions_CanSerializeEnableWebSocketResponses_WithSdkOptions() { @@ -1257,7 +1283,7 @@ public void ModelSwitchRequests_DistinguishRequiredNullFromOmittedOptionalValue( Assert.True(switchAutoTierDocument.RootElement.TryGetProperty("autoTier", out var requiredAutoTier)); Assert.Equal(JsonValueKind.Null, requiredAutoTier.ValueKind); - var switchToType = assembly.GetType("GitHub.Copilot.Rpc.ModelSwitchToRequest"); + var switchToType = assembly.GetType("GitHub.Copilot.Rpc.ModelSwitchToRequestWithSession"); Assert.NotNull(switchToType); var switchToRequest = CreateInternalRequest( switchToType!, diff --git a/dotnet/test/Unit/SessionEventDispatchTests.cs b/dotnet/test/Unit/SessionEventDispatchTests.cs new file mode 100644 index 0000000000..67c4779cf6 --- /dev/null +++ b/dotnet/test/Unit/SessionEventDispatchTests.cs @@ -0,0 +1,221 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public class SessionEventDispatchTests +{ + public static IEnumerable EventTypes => + typeof(SessionEvent).GetCustomAttributes() + .Select(attribute => new object[] { attribute.TypeDiscriminator!, attribute.DerivedType }); + + public static IEnumerable EventDiscriminators => + EventTypes.Select(eventType => new object[] { eventType[0] }); + + [Theory] + [MemberData(nameof(EventTypes))] + public void FromJson_DispatchesEveryRegisteredEvent(string discriminator, Type expectedType) + { + var json = $$""" + { + "data":null, + "agentId":"child-agent", + "ephemeral":true, + "id":"11111111-1111-1111-1111-111111111111", + "parentId":"22222222-2222-2222-2222-222222222222", + "timestamp":"2026-09-30T00:00:00Z", + "type":"{{discriminator}}" + } + """; + + var result = SessionEvent.FromJson(json); + + Assert.Equal(expectedType, result.GetType()); + Assert.Equal(discriminator, result.Type); + Assert.Equal("child-agent", result.AgentId); + Assert.True(result.Ephemeral); + Assert.Equal(Guid.Parse("11111111-1111-1111-1111-111111111111"), result.Id); + Assert.Equal(Guid.Parse("22222222-2222-2222-2222-222222222222"), result.ParentId); + Assert.Equal(DateTimeOffset.Parse("2026-09-30T00:00:00Z"), result.Timestamp); + } + + [Theory] + [MemberData(nameof(EventDiscriminators))] + public void FromJson_DoesNotDispatchPartialDiscriminators(string discriminator) + { + var candidates = Enumerable.Range(0, discriminator.Length) + .Select(index => discriminator[..index] + "?" + discriminator[(index + 1)..]) + .Prepend(discriminator[..^1]) + .Append(discriminator + "x"); + + foreach (var candidate in candidates) + { + var json = $$"""{"type":"{{candidate}}","agentId":"child","data":null}"""; + var actual = SessionEvent.FromJson(json); + + Assert.IsType(actual); + Assert.Equal("child", actual.AgentId); + Assert.Equal(JsonSerializer.Deserialize(json, PolymorphicTypeInfo)?.ToJson(), actual.ToJson()); + } + } + + [Theory] + [InlineData("""{"type":"user.message","data":{"content":"hello"}}""")] + [InlineData("""{"data":{"nested":[{"type":"session.idle"}],"content":"hello"},"type":"user.message"}""")] + [InlineData("""{"ty\u0070e":"user.message","data":{"content":"hello"}}""")] + [InlineData("""{"type":"user\u002Emessage","data":{"content":"hello"}}""")] + [InlineData("""{"type":"future.\u00E9","agentId":"child"}""")] + [InlineData("""{"TYPE":"user.message","data":{"content":"hello"}}""")] + [InlineData("""{"type":"USER.MESSAGE","data":{"content":"hello"}}""")] + [InlineData("""{"type":"future.event","agentId":"child","ephemeral":true,"data":[null,{"type":"user.message"}]}""")] + [InlineData("""{"agentId":"child","ephemeral":true,"data":{"type":"user.message"}}""")] + [InlineData("""{"type":42,"agentId":"child","ephemeral":true}""")] + [InlineData("""{"type":-2147483648}""")] + [InlineData("""{"type":2147483647}""")] + [InlineData("""{"type":"user.message","DATA":{"CONTENT":"hello"}}""")] + [InlineData("""{"type":"user.message","data":{"content":"hello"},"data":{"content":"last"}}""")] + [InlineData("null")] + public void FromJson_PreservesPolymorphicDeserialization(string json) + { + var expected = JsonSerializer.Deserialize(json, PolymorphicTypeInfo); + var actual = SessionEvent.FromJson(json); + + Assert.Equal(expected?.GetType(), actual?.GetType()); + Assert.True(JsonNode.DeepEquals( + JsonNode.Parse(expected?.ToJson() ?? "null"), + JsonNode.Parse(actual?.ToJson() ?? "null"))); + } + + [Theory] + [InlineData("""{"type":null}""")] + [InlineData("""{"type":true}""")] + [InlineData("""{"type":{}}""")] + [InlineData("""{"type":[]}""")] + [InlineData("""{"type":1.5}""")] + [InlineData("""{"type":2147483648}""")] + [InlineData("""{"type":"\uD800"}""")] + [InlineData("""{"type":"session.idle","\uD800":true,"data":{}}""")] + [InlineData("""{"type":"user.message","data":{"content":"hello"}""")] + [InlineData("""{"type":"user.message","data":{"content":"hello"},}""")] + [InlineData("""{"type":"future.event","ty\u0070e":"future.event"}""")] + [InlineData("""{"\u0024future":true,"type":"session.idle","data":{}}""")] + [InlineData("""{"type":"user.message","type":"session.idle","data":{"content":"hello"}}""")] + [InlineData("""{"type":"user.message","data":{"content":"hello"},"type":"user.message"}""")] + [InlineData("""{"type":"future.event","data":{},"type":"future.event"}""")] + [InlineData("""{"type":"user.message","data":{}}""")] + [InlineData("""{"type":"user.message"}""")] + [InlineData("""{"$id":"1","type":"user.message","data":{"content":"hello"}}""")] + [InlineData("""{"type":"user.message","data":{"content":"hello"},"$ref":"1"}""")] + [InlineData("""{"type":"future.event","$values":[]}""")] + [InlineData("""{"$future":"ignored","type":"user.message","data":{"content":"hello"}}""")] + [InlineData("""{"type":"session.idle","id":"invalid","data":{}}""")] + [InlineData("""{"type":"session.idle","timestamp":"invalid","data":{}}""")] + [InlineData("""{"type":"session.idle","ephemeral":"invalid","data":{}}""")] + [InlineData("""[{"type":"session.idle","data":{}}]""")] + [InlineData("42")] + [InlineData("true")] + [InlineData("@")] + [InlineData("")] + [InlineData("\"text\"")] + [InlineData("""{"type":"user.message","data":{"content":"hello"}} trailing""")] + public void FromJson_RejectsInvalidEventsLikePolymorphicDeserialization(string json) + { + Assert.Throws(() => JsonSerializer.Deserialize(json, PolymorphicTypeInfo)); + Assert.Throws(() => SessionEvent.FromJson(json)); + } + + [Theory] + [InlineData(0)] + [InlineData(32)] + [InlineData(256)] + [InlineData(100_000)] + public void FromJson_PreservesUnicodeAcrossBufferSizes(int repetitions) + { + var content = string.Concat(Enumerable.Repeat("a\u00E9\u4E2D\U0001F600", repetitions)); + var json = "{\"data\":{\"content\":\"" + content + "\"},\"ty\\u0070e\":\"user.message\"}"; + + var actual = Assert.IsType(SessionEvent.FromJson(json)); + + Assert.Equal(content, actual.Data.Content); + Assert.Equal(JsonSerializer.Deserialize(json, PolymorphicTypeInfo)?.ToJson(), actual.ToJson()); + } + + [Theory] + [InlineData('\uD800', 0)] + [InlineData('\uDC00', 0)] + [InlineData('\uD800', 1000)] + [InlineData('\uDC00', 1000)] + public void FromJson_RejectsInvalidUtf16LikePolymorphicDeserialization(char invalid, int padding) + { + var json = "{\"type\":\"user.message\",\"data\":{\"content\":\"" + new string('a', padding) + invalid + "\"}}"; + + Assert.Throws(() => JsonSerializer.Deserialize(json, PolymorphicTypeInfo)); + Assert.Throws(() => SessionEvent.FromJson(json)); + } + + [Fact] + public void FromJson_RejectsNullInputLikePolymorphicDeserialization() + { + Assert.Throws(() => JsonSerializer.Deserialize((string)null!, PolymorphicTypeInfo)); + Assert.Throws(() => SessionEvent.FromJson(null!)); + } + + [Fact] + public async Task FromJson_IsSafeForConcurrentDeserialization() + { + var events = EventTypes.ToArray(); + var tasks = Enumerable.Range(0, 32).Select(i => Task.Run(() => + { + foreach (var item in events) + { + var discriminator = (string)item[0]; + var expectedType = (Type)item[1]; + var json = $$"""{"type":"{{discriminator}}","agentId":"{{i}}","data":null}"""; + var actual = SessionEvent.FromJson(json); + + Assert.Equal(expectedType, actual.GetType()); + Assert.Equal(i.ToString(), actual.AgentId); + } + })); + + await Task.WhenAll(tasks); + } + + [Fact] + public void LiveOptions_SerializeEventsWithoutChangingTheWireFormat() + { + var options = (JsonSerializerOptions)typeof(CopilotClient).GetProperty( + "SerializerOptionsForMessageFormatter", BindingFlags.Static | BindingFlags.NonPublic)! + .GetValue(null)!; + var sessionEvent = new UserMessageEvent + { + Id = Guid.Parse("11111111-1111-1111-1111-111111111111"), + Timestamp = DateTimeOffset.Parse("2026-09-30T00:00:00Z"), + Data = new UserMessageData { Content = "hello" }, + }; + + var json = JsonSerializer.Serialize(sessionEvent, options); + Assert.True(JsonNode.DeepEquals(JsonNode.Parse(sessionEvent.ToJson()), JsonNode.Parse(json))); + var decoded = Assert.IsType(JsonSerializer.Deserialize(json, options)); + Assert.Equal("hello", decoded.Data.Content); + } + + // The unchanged source-generated context is the reference contract, not reflection metadata. + private static JsonTypeInfo PolymorphicTypeInfo + { + get + { + var contextType = typeof(SessionEvent).Assembly.GetType("GitHub.Copilot.SessionEventsJsonContext", true)!; + var context = (JsonSerializerContext)contextType.GetProperty("Default")!.GetValue(null)!; + return (JsonTypeInfo)context.GetTypeInfo(typeof(SessionEvent))!; + } + } +} diff --git a/dotnet/test/Unit/SetToolsTests.cs b/dotnet/test/Unit/SetToolsTests.cs new file mode 100644 index 0000000000..c88dccb639 --- /dev/null +++ b/dotnet/test/Unit/SetToolsTests.cs @@ -0,0 +1,923 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +#if NET8_0_OR_GREATER +#pragma warning disable GHCP001 // Live tool replacement is intentionally experimental. + +using Microsoft.Extensions.AI; +using Microsoft.Extensions.Logging; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.ExceptionServices; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization.Metadata; +using Xunit; + +namespace GitHub.Copilot.Test.Unit; + +public sealed class SetToolsTests +{ + [Fact] + public async Task SetToolsAsync_Sends_Complete_Payload_And_Empty_Descriptions() + { + await using var server = await SetToolsFakeServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig()); + server.ClearRequests(); + + await session.SetToolsAsync([ + CopilotTool.DefineTool( + (int code) => $"fruit:{code}", + new CopilotToolOptions + { + OverridesBuiltInTool = true, + SkipPermission = true, + IsTerminal = true, + Defer = CopilotToolDefer.Never, + Metadata = new Dictionary { ["owner"] = "test" }, + }, + new AIFunctionFactoryOptions + { + Name = "lookup_fruit", + Description = "Looks up the fruit for a numeric code", + }), + AIFunctionFactory.Create(() => "empty", new AIFunctionFactoryOptions + { + Name = "empty_description", + Description = string.Empty, + }), + ]); + + var request = Assert.Single(server.Requests, request => request.Method == "session.tools.set"); + Assert.Equal(session.SessionId, request.Params.GetProperty("sessionId").GetString()); + var tools = request.Params.GetProperty("tools").EnumerateArray().ToList(); + Assert.Collection( + tools, + fruit => + { + Assert.Equal("lookup_fruit", fruit.GetProperty("name").GetString()); + Assert.Equal("Looks up the fruit for a numeric code", fruit.GetProperty("description").GetString()); + Assert.Equal("object", fruit.GetProperty("parameters").GetProperty("type").GetString()); + Assert.True(fruit.GetProperty("parameters").GetProperty("properties").TryGetProperty("code", out _)); + Assert.True(fruit.GetProperty("overridesBuiltInTool").GetBoolean()); + Assert.True(fruit.GetProperty("skipPermission").GetBoolean()); + Assert.True(fruit.GetProperty("isTerminal").GetBoolean()); + Assert.Equal("never", fruit.GetProperty("defer").GetString()); + Assert.Equal("test", fruit.GetProperty("metadata").GetProperty("owner").GetString()); + }, + empty => + { + Assert.Equal("empty_description", empty.GetProperty("name").GetString()); + Assert.Equal(string.Empty, empty.GetProperty("description").GetString()); + Assert.False(empty.TryGetProperty("overridesBuiltInTool", out _)); + Assert.False(empty.TryGetProperty("skipPermission", out _)); + Assert.False(empty.TryGetProperty("isTerminal", out _)); + Assert.False(empty.TryGetProperty("defer", out _)); + Assert.False(empty.TryGetProperty("metadata", out _)); + }); + } + + [Fact] + public async Task SetToolsAsync_Switches_Handlers_After_Acceptance() + { + await using var server = await SetToolsFakeServer.StartAsync(); + var releaseSet = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + server.BeforeSetToolsResponseAsync = _ => releaseSet.Task; + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + Tools = [Tool("replaceable_tool", "old")], + }); + server.ClearRequests(); + + var replacement = session.SetToolsAsync([Tool("replaceable_tool", "new")]); + await server.WaitForRequestAsync("session.tools.set"); + + var oldResult = await InvokeToolAsync(server, session, "replaceable_tool", "old-request"); + Assert.Equal("old", oldResult); + + releaseSet.SetResult(); + await replacement; + + var newResult = await InvokeToolAsync(server, session, "replaceable_tool", "new-request"); + Assert.Equal("new", newResult); + } + + [Fact] + public async Task SetToolsAsync_Rejection_Leaves_Handlers_Unchanged() + { + await using var server = await SetToolsFakeServer.StartAsync(); + server.RejectNextSetTools("invalid tool name"); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + Tools = [Tool("stable_tool", "old")], + }); + + await Assert.ThrowsAsync(() => session.SetToolsAsync([Tool("stable_tool", "new")])); + + var result = await InvokeToolAsync(server, session, "stable_tool", "stable-request"); + Assert.Equal("old", result); + } + + [Fact] + public async Task SetToolsAsync_Acceptance_After_Disposal_Cannot_Restore_Handlers_Or_Close_Connection() + { + await using var server = await SetToolsFakeServer.StartAsync(); + var releaseSet = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + server.BeforeSetToolsResponseAsync = _ => releaseSet.Task; + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig()); + var replacement = session.SetToolsAsync([Tool("retired_tool", "must-not-install")]); + var timeout = TimeSpan.FromSeconds(5); + Exception? bodyFailure = null; + try + { + await server.WaitForRequestAsync("session.tools.set").WaitAsync(timeout); + await session.DisposeAsync().AsTask().WaitAsync(timeout); + releaseSet.TrySetResult(); + + await Assert.ThrowsAsync(() => replacement.WaitAsync(timeout)); + await using var liveSession = await client.CreateSessionAsync(new SessionConfig()).WaitAsync(timeout); + Assert.NotEqual(session.SessionId, liveSession.SessionId); + } + catch (Exception error) + { + bodyFailure = error; + } + finally + { + releaseSet.TrySetResult(); + try + { + await replacement.WaitAsync(timeout); + } + catch (ObjectDisposedException) + { + // The acknowledged replacement was rejected by local retirement. + } + catch (Exception cleanupError) + { + bodyFailure = bodyFailure is null ? cleanupError : new AggregateException(bodyFailure, cleanupError); + } + } + if (bodyFailure is not null) + { + ExceptionDispatchInfo.Capture(bodyFailure).Throw(); + } + } + + [Fact] + public async Task SetToolsAsync_Empty_Set_Removes_Handlers() + { + await using var server = await SetToolsFakeServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + Tools = [Tool("removed_tool", "old")], + }); + + await session.SetToolsAsync([]); + + var setRequest = server.Requests.Last(request => request.Method == "session.tools.set"); + Assert.Empty(setRequest.Params.GetProperty("tools").EnumerateArray()); + Assert.Null(GetTool(session, "removed_tool")); + } + + [Fact] + public async Task SetToolsAsync_Serializes_Concurrent_Replacements_And_Allows_Later_After_Rejection() + { + await using var server = await SetToolsFakeServer.StartAsync(); + var releaseFirst = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var setCall = 0; + server.BeforeSetToolsResponseAsync = _ => Interlocked.Increment(ref setCall) == 1 ? releaseFirst.Task : Task.CompletedTask; + server.RejectSetToolsCall(1, "first failed"); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + Tools = [Tool("ordered_tool", "initial")], + }); + server.ClearRequests(); + + var first = session.SetToolsAsync([Tool("ordered_tool", "first")]); + await server.WaitForRequestAsync("session.tools.set"); + var second = session.SetToolsAsync([Tool("ordered_tool", "second")]); + await Task.Delay(100); + Assert.Single(server.Requests, request => request.Method == "session.tools.set"); + + releaseFirst.SetResult(); + await Assert.ThrowsAsync(() => first); + await second; + + var setRequests = server.Requests.Where(request => request.Method == "session.tools.set").ToList(); + Assert.Equal(2, setRequests.Count); + Assert.Equal("first failed", server.RejectedMessages.Single()); + var result = await InvokeToolAsync(server, session, "ordered_tool", "ordered-request"); + Assert.Equal("second", result); + } + + [Fact] + public async Task SetToolsAsync_Cancellation_After_Request_Was_Sent_Still_Installs_Accepted_Replacement() + { + await using var server = await SetToolsFakeServer.StartAsync(); + var releaseSet = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + server.BeforeSetToolsResponseAsync = _ => releaseSet.Task; + using var acceptanceContext = new DeferredSynchronizationContext(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + Tools = [AIFunctionFactory.Create(() => + { + acceptanceContext.Resume(); + return "old"; + }, new AIFunctionFactoryOptions { Name = "cancel_tool", Description = "Returns old" })], + }); + + try + { + using var cts = new CancellationTokenSource(); + var replacement = acceptanceContext.Run(() => session.SetToolsAsync([Tool("cancel_tool", "new")], cts.Token)); + await server.WaitForRequestAsync("session.tools.set"); + await cts.CancelAsync(); + await acceptanceContext.RunNextAsync(); + await Assert.ThrowsAnyAsync(() => replacement); + + releaseSet.SetResult(); + await acceptanceContext.WaitForPendingAsync(); + using var acceptanceTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); + string? result; + try + { + result = await InvokeToolUntilAsync(server, session, "cancel_tool", "cancel-request", "new", acceptanceTimeout.Token); + } + catch (OperationCanceledException ex) when (acceptanceTimeout.IsCancellationRequested) + { + throw new TimeoutException(ex.Message, ex); + } + Assert.Equal("new", result); + } + finally + { + releaseSet.TrySetResult(); + acceptanceContext.Resume(); + } + } + + [Fact] + public async Task InvokeToolUntilAsync_Rejects_Expected_Result_After_Deadline() + { + using var deadline = new CancellationTokenSource(); + await using var server = await SetToolsFakeServer.StartAsync(); + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + Tools = [AIFunctionFactory.Create(() => + { + deadline.Cancel(); + return "new"; + }, new AIFunctionFactoryOptions { Name = "late_tool", Description = "Returns after the wait deadline" })], + }); + + await Assert.ThrowsAnyAsync(() => + InvokeToolUntilAsync(server, session, "late_tool", "late-request", "new", deadline.Token)); + + var response = await server.WaitForRequestAsync("session.tools.handlePendingToolCall", "late-request-0"); + Assert.Equal("new", response.Params.GetProperty("result").GetProperty("textResultForLlm").GetString()); + } + + [Fact] + public async Task SetToolsAsync_Publishes_Accepted_Replacement_Before_Next_Tool_Request() + { + await using var server = await SetToolsFakeServer.StartAsync(); + var logger = new SetToolsResponseLogger(); + var releaseSet = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + server.BeforeSetToolsResponseAsync = _ => releaseSet.Task; + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url), Logger = logger }); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + Tools = [Tool("cancel_tool", "old")], + }); + + using var cts = new CancellationTokenSource(); + try + { + var replacement = session.SetToolsAsync([Tool("cancel_tool", "new")], cts.Token); + await server.WaitForRequestAsync("session.tools.set"); + await cts.CancelAsync(); + await Assert.ThrowsAnyAsync(() => replacement); + + releaseSet.SetResult(); + await logger.ResponseReceived.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + var result = await InvokeToolAsync(server, session, "cancel_tool", "cancel-request"); + Assert.Equal("new", result); + } + finally + { + releaseSet.TrySetResult(); + logger.ContinueResponse.TrySetResult(); + } + } + + [Fact] + public async Task SetToolsAsync_Cancellation_While_Queued_Does_Not_Send_Request() + { + await using var server = await SetToolsFakeServer.StartAsync(); + var releaseSet = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + server.BeforeSetToolsResponseAsync = _ => releaseSet.Task; + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig + { + Tools = [Tool("queued_tool", "old")], + }); + server.ClearRequests(); + + var first = session.SetToolsAsync([Tool("queued_tool", "first")]); + await server.WaitForRequestAsync("session.tools.set"); + + using var cts = new CancellationTokenSource(); + var queued = session.SetToolsAsync([Tool("queued_tool", "queued")], cts.Token); + await cts.CancelAsync(); + await Assert.ThrowsAnyAsync(() => queued); + + using var preCanceled = new CancellationTokenSource(); + await preCanceled.CancelAsync(); + await Assert.ThrowsAnyAsync(() => + session.SetToolsAsync([Tool("queued_tool", "pre-canceled")], preCanceled.Token)); + + releaseSet.SetResult(); + await first; + + Assert.Single(server.Requests, request => request.Method == "session.tools.set"); + var result = await InvokeToolAsync(server, session, "queued_tool", "queued-request"); + Assert.Equal("first", result); + } + + [Fact] + public async Task SetToolsAsync_Cancellation_Before_Queued_Waiter_Resumes_Does_Not_Send_Request() + { + await using var server = await SetToolsFakeServer.StartAsync(); + var releaseSet = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + server.BeforeSetToolsResponseAsync = _ => releaseSet.Task; + await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) }); + await using var session = await client.CreateSessionAsync(new SessionConfig()); + + var first = session.SetToolsAsync([Tool("queued_tool", "first")]); + await server.WaitForRequestAsync("session.tools.set"); + using var cts = new CancellationTokenSource(); + var context = new PausedContinuationContext(); + var previousContext = SynchronizationContext.Current; + Task queued; + try + { + SynchronizationContext.SetSynchronizationContext(context); + queued = session.SetToolsAsync([Tool("queued_tool", "queued")], cts.Token); + } + finally + { + SynchronizationContext.SetSynchronizationContext(previousContext); + releaseSet.TrySetResult(); + } + + await first; + var continuation = await context.Continuation.Task.WaitAsync(TimeSpan.FromSeconds(5)); + try + { + await cts.CancelAsync(); + await Assert.ThrowsAnyAsync(() => queued); + } + finally + { + continuation.Callback(continuation.State); + } + + // A later completed replacement proves the cancelled waiter has left the queue. + await session.SetToolsAsync([Tool("queued_tool", "last")]); + Assert.Equal( + ["Returns first", "Returns last"], + server.Requests.Where(request => request.Method == "session.tools.set") + .Select(request => request.Params.GetProperty("tools")[0].GetProperty("description").GetString())); + } + + private static AIFunction Tool(string name, string result) + { + return AIFunctionFactory.Create( + () => result, + new AIFunctionFactoryOptions { Name = name, Description = $"Returns {result}" }); + } + + private static AIFunction? GetTool(CopilotSession session, string name) + { + var method = typeof(CopilotSession).GetMethod("GetTool", BindingFlags.Instance | BindingFlags.NonPublic) + ?? throw new InvalidOperationException("GetTool method was not found."); + return (AIFunction?)method.Invoke(session, [name]); + } + + private static async Task InvokeToolAsync( + SetToolsFakeServer server, + CopilotSession session, + string toolName, + string requestId, + CancellationToken cancellationToken = default) + { + await SendToolRequestAsync(server, session, toolName, requestId, cancellationToken); + var request = await server.WaitForRequestAsync("session.tools.handlePendingToolCall", requestId, cancellationToken); + return request.Params.GetProperty("result").GetProperty("textResultForLlm").GetString(); + } + + /// + /// Invokes until the replacement handler answers it, or a timeout expires. + /// + /// + /// Writing a session.tools.set response happens before the client has read it, completed the RPC, + /// and published the replacement handlers. Tests that + /// await a successful SetToolsAsync task complete after that publish and need no polling; the + /// cancellation test cannot, because the caller's await threw and the replacement task runs detached. + /// Polling does not weaken the assertion: a replacement that never installs still fails, on the timeout. + /// + private static async Task InvokeToolUntilAsync( + SetToolsFakeServer server, + CopilotSession session, + string toolName, + string requestId, + string expected, + CancellationToken cancellationToken) + { + var attempt = 0; + string? result = null; + try + { + while (true) + { + // Each invocation needs its own id: the server records one pending call per request id. + result = await InvokeToolAsync(server, session, toolName, $"{requestId}-{attempt++}", cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + if (result == expected) + { + return result; + } + + Assert.Equal("old", result); + await Task.Delay(20, cancellationToken); + } + } + catch (OperationCanceledException ex) when (cancellationToken.IsCancellationRequested) + { + throw new OperationCanceledException( + $"Accepted replacement did not install '{expected}'; last tool result was '{result ?? ""}'.", + ex, + cancellationToken); + } + } + + private static Task SendToolRequestAsync( + SetToolsFakeServer server, + CopilotSession session, + string toolName, + string requestId, + CancellationToken cancellationToken = default) + { + using var arguments = JsonDocument.Parse("{}"); + return server.SendSessionEventAsync(session.SessionId, "external_tool.requested", new Dictionary + { + ["sessionId"] = session.SessionId, + ["requestId"] = requestId, + ["toolCallId"] = requestId + "-call", + ["toolName"] = toolName, + ["arguments"] = arguments.RootElement.Clone(), + }, cancellationToken); + } + + private sealed record RpcRequestRecord(string Method, JsonElement Params); + + private sealed class DeferredSynchronizationContext : SynchronizationContext, IDisposable + { + private readonly object _gate = new(); + private readonly Queue<(SendOrPostCallback Callback, object? State)> _callbacks = new(); + private readonly SemaphoreSlim _available = new(0); + private bool _resumed; + + public Task Run(Func action) + { + var previous = Current; + SetSynchronizationContext(this); + try + { + return action(); + } + finally + { + SetSynchronizationContext(previous); + } + } + + public override void Post(SendOrPostCallback callback, object? state) + { + lock (_gate) + { + if (!_resumed) + { + _callbacks.Enqueue((callback, state)); + _available.Release(); + return; + } + } + ThreadPool.QueueUserWorkItem(_ => callback(state)); + } + + public async Task WaitForPendingAsync() + { + if (!await _available.WaitAsync(TimeSpan.FromSeconds(5))) + { + throw new TimeoutException("The client did not post its tool-replacement continuation."); + } + } + + public async Task RunNextAsync() + { + await WaitForPendingAsync(); + (SendOrPostCallback Callback, object? State) next; + lock (_gate) + { + next = _callbacks.Dequeue(); + } + next.Callback(next.State); + } + + public void Resume() + { + (SendOrPostCallback Callback, object? State)[] pending; + lock (_gate) + { + _resumed = true; + pending = _callbacks.ToArray(); + _callbacks.Clear(); + } + foreach (var (callback, state) in pending) + { + ThreadPool.QueueUserWorkItem(_ => callback(state)); + } + } + + public void Dispose() + { + Resume(); + _available.Dispose(); + } + } + + private sealed class PausedContinuationContext : SynchronizationContext + { + public TaskCompletionSource<(SendOrPostCallback Callback, object? State)> Continuation { get; } = + new(TaskCreationOptions.RunContinuationsAsynchronously); + + public override void Post(SendOrPostCallback callback, object? state) + { + if (!Continuation.TrySetResult((callback, state))) + { + base.Post(callback, state); + } + } + } + + private sealed class SetToolsResponseLogger : ILogger + { + public TaskCompletionSource ResponseReceived { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously); + public TaskCompletionSource ContinueResponse { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously); + public IDisposable? BeginScope(TState state) where TState : notnull => null; + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (state is IReadOnlyList> properties && + properties.Any(entry => entry is { Key: "Method", Value: "session.tools.set" }) && + properties.Any(entry => entry is { Key: "Status", Value: "Succeeded" })) + { + // Hold the RPC awaiter after acceptance without blocking the reader's next tool event. + ResponseReceived.TrySetResult(); + ContinueResponse.Task.GetAwaiter().GetResult(); + } + } + } + + private sealed class SetToolsFakeServer : IAsyncDisposable + { + private static readonly JsonSerializerOptions s_jsonOptions = new(JsonSerializerDefaults.Web) + { + TypeInfoResolver = new DefaultJsonTypeInfoResolver(), + }; + private readonly TcpListener _listener; + private readonly CancellationTokenSource _cts = new(); + private readonly SemaphoreSlim _writeLock = new(1, 1); + private readonly List _requests = []; + private readonly object _requestsLock = new(); + private readonly Task _serverTask; + private readonly List _requestTasks = []; + private NetworkStream? _stream; + private string? _nextSetToolsRejection; + private readonly Dictionary _setToolsRejections = []; + private int _setToolsCalls; + + private SetToolsFakeServer(TcpListener listener) + { + _listener = listener; + _serverTask = RunAsync(); + } + + public string Url + { + get + { + var endpoint = (IPEndPoint)_listener.LocalEndpoint; + return $"http://127.0.0.1:{endpoint.Port}"; + } + } + + public Func? BeforeSetToolsResponseAsync { get; set; } + + public List RejectedMessages { get; } = []; + + public IReadOnlyList Requests + { + get + { + lock (_requestsLock) + { + return _requests.ToArray(); + } + } + } + + public static Task StartAsync() + { + var listener = new TcpListener(IPAddress.Loopback, 0); + listener.Start(); + return Task.FromResult(new SetToolsFakeServer(listener)); + } + + public void ClearRequests() + { + lock (_requestsLock) + { + _requests.Clear(); + } + } + + public void RejectNextSetTools(string message) + { + _nextSetToolsRejection = message; + } + + public void RejectSetToolsCall(int callNumber, string message) + { + _setToolsRejections[callNumber] = message; + } + + public async Task WaitForRequestAsync( + string method, + string? requestId = null, + CancellationToken cancellationToken = default) + { + using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + timeout.CancelAfter(TimeSpan.FromSeconds(5)); + while (!timeout.IsCancellationRequested) + { + cancellationToken.ThrowIfCancellationRequested(); + var request = Requests.FirstOrDefault(request => + request.Method == method && + (requestId is null || request.Params.GetProperty("requestId").GetString() == requestId)); + if (request is not null) + { + return request; + } + + await Task.Delay(20, cancellationToken); + } + + cancellationToken.ThrowIfCancellationRequested(); + throw new TimeoutException($"Timed out waiting for RPC method '{method}'."); + } + + public async Task SendSessionEventAsync( + string sessionId, + string type, + Dictionary data, + CancellationToken cancellationToken = default) + { + using var linkedCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cts.Token); + var stream = _stream ?? throw new InvalidOperationException("Client is not connected."); + var evt = new Dictionary + { + ["id"] = Guid.NewGuid().ToString(), + ["timestamp"] = DateTimeOffset.UtcNow.ToString("O"), + ["parentId"] = null, + ["type"] = type, + ["data"] = data, + }; + await WriteMessageAsync(stream, new Dictionary + { + ["jsonrpc"] = "2.0", + ["method"] = "session.event", + ["params"] = new Dictionary + { + ["sessionId"] = sessionId, + ["event"] = evt, + }, + }, linkedCancellation.Token); + } + + public async ValueTask DisposeAsync() + { + _cts.Cancel(); + _listener.Stop(); + try + { + await _serverTask; + } + catch (Exception ex) when (ex is OperationCanceledException or ObjectDisposedException or IOException or SocketException) + { + } + + try + { + await Task.WhenAll(_requestTasks).WaitAsync(TimeSpan.FromSeconds(5)); + } + catch (OperationCanceledException) when (_requestTasks.All(task => task.IsCanceled || task.IsCompletedSuccessfully)) + { + // Shutdown cancellation may stop a writer, but every owned request must finish. + } + finally + { + _cts.Dispose(); + _writeLock.Dispose(); + } + } + + private async Task RunAsync() + { + using var tcpClient = await _listener.AcceptTcpClientAsync(_cts.Token); + using var stream = tcpClient.GetStream(); + _stream = stream; + + while (!_cts.Token.IsCancellationRequested) + { + using var message = await ReadMessageAsync(stream, _cts.Token); + if (message is null) + { + return; + } + + var root = message.RootElement; + if (!root.TryGetProperty("method", out _) || !root.TryGetProperty("id", out var id)) + { + continue; + } + + _requestTasks.Add(HandleRequestAsync(stream, root, id.Clone(), _cts.Token)); + } + } + + private async Task HandleRequestAsync(Stream stream, JsonElement request, JsonElement id, CancellationToken cancellationToken) + { + var method = request.GetProperty("method").GetString()!; + var parameters = request.TryGetProperty("params", out var rawParams) + ? rawParams.Clone() + : JsonDocument.Parse("{}").RootElement.Clone(); + var requestRecord = new RpcRequestRecord(method, parameters); + lock (_requestsLock) + { + _requests.Add(requestRecord); + } + + if (method == "session.tools.set") + { + var callNumber = Interlocked.Increment(ref _setToolsCalls); + if (BeforeSetToolsResponseAsync is { } beforeResponse) + { + await beforeResponse(requestRecord); + } + + var rejection = _nextSetToolsRejection; + _nextSetToolsRejection = null; + if (rejection is null && _setToolsRejections.TryGetValue(callNumber, out var configuredRejection)) + { + rejection = configuredRejection; + } + + if (rejection is not null) + { + RejectedMessages.Add(rejection); + await WriteErrorAsync(stream, id, -32602, rejection, cancellationToken); + return; + } + } + + var result = method switch + { + "connect" => new Dictionary + { + ["ok"] = true, + ["protocolVersion"] = 3, + ["version"] = "test", + }, + "session.create" => new Dictionary + { + ["sessionId"] = parameters.TryGetProperty("sessionId", out var sid) && sid.ValueKind == JsonValueKind.String + ? sid.GetString() + : Guid.NewGuid().ToString(), + ["workspacePath"] = null, + ["capabilities"] = null, + }, + "session.detach" => new Dictionary { ["success"] = true }, + "session.tools.set" => new Dictionary(), + "session.tools.handlePendingToolCall" => new Dictionary { ["success"] = true }, + _ => throw new InvalidOperationException($"Unexpected RPC method '{method}'."), + }; + + await WriteMessageAsync(stream, new Dictionary + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["result"] = result, + }, cancellationToken); + } + + private Task WriteErrorAsync(Stream stream, JsonElement id, int code, string message, CancellationToken cancellationToken) + { + return WriteMessageAsync(stream, new Dictionary + { + ["jsonrpc"] = "2.0", + ["id"] = id, + ["error"] = new Dictionary + { + ["code"] = code, + ["message"] = message, + }, + }, cancellationToken); + } + + private async Task WriteMessageAsync(Stream stream, object payload, CancellationToken cancellationToken) + { + var body = JsonSerializer.SerializeToUtf8Bytes(payload, s_jsonOptions); + var header = Encoding.ASCII.GetBytes($"Content-Length: {body.Length}\r\n\r\n"); + await _writeLock.WaitAsync(cancellationToken); + try + { + await stream.WriteAsync(header, cancellationToken); + await stream.WriteAsync(body, cancellationToken); + await stream.FlushAsync(cancellationToken); + } + finally + { + _writeLock.Release(); + } + } + + private static async Task ReadMessageAsync(Stream stream, CancellationToken cancellationToken) + { + var header = new List(); + var buffer = new byte[1]; + while (true) + { + var read = await stream.ReadAsync(buffer, cancellationToken); + if (read == 0) + { + return null; + } + + header.Add(buffer[0]); + if (header.Count >= 4 && + header[^4] == '\r' && header[^3] == '\n' && header[^2] == '\r' && header[^1] == '\n') + { + break; + } + } + + var headerText = Encoding.ASCII.GetString(header.ToArray()); + var lengthLine = headerText.Split("\r\n", StringSplitOptions.RemoveEmptyEntries) + .Single(line => line.StartsWith("Content-Length:", StringComparison.OrdinalIgnoreCase)); + var length = int.Parse(lengthLine["Content-Length:".Length..].Trim(), System.Globalization.CultureInfo.InvariantCulture); + var body = new byte[length]; + var offset = 0; + while (offset < length) + { + var read = await stream.ReadAsync(body.AsMemory(offset, length - offset), cancellationToken); + if (read == 0) + { + return null; + } + + offset += read; + } + + return JsonDocument.Parse(body); + } + } +} + +#pragma warning restore GHCP001 +#endif diff --git a/go/README.md b/go/README.md index 4ff2b12aef..fe297f069b 100644 --- a/go/README.md +++ b/go/README.md @@ -43,6 +43,12 @@ registers a Mission Control environment. Both can be enabled. Import `rpc` from Local hostname, port, token, and authentication settings belong inside `LocalServer`. The returned handle's `URL`, `Token`, `PID`, and `EnvironmentID` are optional pointers; Mission Control-only hosting has no local URL. These APIs are experimental. +Call `host.ListSessions(ctx)` to read the complete advertised catalog of live and +dormant sessions. +Set top-level `ComputeID` to reuse the durable catalog across local and Mission +Control hosting; it must agree with `GitHubEnvironment.ComputeID` when both are +supplied. The runtime persists an omitted local-only identity. Publication +survives host shutdown; dormant application sessions require your resume callback. Use the generated `client.RPC.Environments` namespace for environment list/get/delete operations. See [runtime-supervised AHP hosting](../docs/runtime-supervised-host.md) for creation and resume callbacks, resident-session publication, ownership, and shared-snapshot E2Es. @@ -145,6 +151,56 @@ Avoid logging it indiscriminately: server-provided data may contain sensitive information. Its fields and data bytes are shared with the wrapped error; copy them before mutation. +## Skill providers (experimental) + +Use `SessionConfig.SkillProvider` or `ResumeSessionConfig.SkillProvider` to +serve session-scoped skills from your application instead of from `SKILL.md` +files on disk. The provider is ephemeral: it is not serialized or persisted, so +re-supply it on every resume. Resuming without a provider unbinds any previous +provider for that session. + + + +```go +type memorySkillProvider struct{} + +func (memorySkillProvider) ListSkills(ctx context.Context) ([]rpc.SkillProviderDescriptor, error) { + return []rpc.SkillProviderDescriptor{{ + Name: "review", + Description: "Review code using the host application's policy", + }}, nil +} + +func (memorySkillProvider) ReadSkill(ctx context.Context, name string) (string, error) { + if name != "review" { + return "", copilot.ErrSkillNotFound + } + return "Review the changes and call out policy violations.", nil +} + +session, err := client.CreateSession(ctx, &copilot.SessionConfig{ + SkillProvider: memorySkillProvider{}, + // Required only when ClientOptions.Mode is ModeEmpty. + // EnableSkills: copilot.Bool(true), +}) +``` + +`ReadSkill` returns an error for which +`errors.Is(err, copilot.ErrSkillNotFound)` is true when a listed skill no +longer exists. Other errors are reported to the runtime as generic provider +failures so provider error text is not exposed to the model. + +Skill providers are not supported for cloud sessions. Creating a cloud session +with a provider fails before the client connects with: +`Skill providers are not supported for cloud sessions.` + +Provider methods can be called concurrently by the runtime, including from +sub-agents that inherit the root session binding. Implementations must be safe +for concurrent calls and should observe the `context.Context`. The context is +cancelled when the runtime abandons the request: the call times out, the +session disconnects or is deleted, or a resume replaces the provider. It is not +cancelled when the connection closes or the client is force-stopped. + ## Installation confirmation (experimental) Set `ClientOptions.InstallationConfirmationHandler` to receive the runtime's @@ -257,7 +313,8 @@ Implemented with pure-Go FFI (via [purego](https://github.com/ebitengine/purego) - `NewClient(options *ClientOptions) *Client` - Create a new client - `Start(ctx context.Context) error` - Start the CLI server - `Stop() error` - Gracefully stop the CLI server. For an owned stdio process, requests runtime shutdown, closes stdin, and waits up to 10 seconds for host cleanup (including telemetry) and natural exit before falling back to a forced termination. -- `ForceStop()` - Forcefully stop without graceful cleanup +- `ForceStop()` - Forcefully stop without graceful RPC cleanup. Retires local event subscriptions and session callbacks, including entries held by dispatch snapshots or unsubscribe handles. Pending response waits fail without waiting for idle or their context deadlines; already-dispatched handlers are not joined. Late event registration is logged and returns a no-op unsubscribe, and a late tool-replacement acknowledgment cannot restore retired handlers. +- A plain `SendAndWait` that has already received its terminal idle or error preserves that result when local session retirement races the wait. An earlier event handler may also call `Disconnect` on the terminal event without losing that result. Without retirement, the wait still lets earlier synchronous handlers finish; a forced stop does not join those handlers or complete waits whose terminal dispatch they are still blocking. - `CreateSession(ctx context.Context, config *SessionConfig) (*Session, error)` - Create a new session - `ResumeSession(ctx context.Context, sessionID string, config *ResumeSessionConfig) (*Session, error)` - Resume an existing session - `ResumeSessionWithOptions(ctx context.Context, sessionID string, config *ResumeSessionConfig) (*Session, error)` - Resume with additional configuration @@ -309,6 +366,7 @@ Event types: `SessionLifecycleCreated`, `SessionLifecycleDeleted`, `SessionLifec - `UseLoggedInUser` (\*bool): Whether to use logged-in user for authentication (default: true, but false when `GitHubToken` is provided). Cannot be used with `URIConnection`. - `EnableRemoteSessions` (bool): Enable remote session support (Mission Control integration). Ignored with `URIConnection`. - `Telemetry` (\*TelemetryConfig): OpenTelemetry configuration for the runtime. Providing this enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below. +- `SessionFS` (\*SessionFSConfig): Custom session filesystem provider configuration. For the `view` tool to read provider-only images, set `Capabilities.Binary` and return a provider implementing `SessionFSBinaryProvider` (`ReadFileBytes` and `WriteFileBytes`) from `CreateSessionFSProvider`. Image reads do not fall back to local files. Binary reads and writes are limited to 50,330,880 bytes before encoding or decoding. **SessionConfig:** @@ -367,6 +425,7 @@ Initial acquisition runs during session creation or resume. Cancellation, provid - `Send(ctx context.Context, options MessageOptions) (string, error)` - Send a message - `On(handler SessionEventHandler) func()` - Subscribe to events (returns unsubscribe function) - `Abort(ctx context.Context) error` - Abort the currently processing message +- `SetTools(ctx context.Context, tools []Tool) error` - Replace this client's live custom tools and handlers for the session. See [changing tools](../docs/features/changing-tools.md) for shared behavior and active-turn limitations. - `GetEvents(ctx context.Context) ([]SessionEvent, error)` - Get event history - `Disconnect() error` - Disconnect the session (releases in-memory resources, preserves disk state) - `UI() *SessionUI` - Interactive UI API for elicitation dialogs @@ -445,6 +504,8 @@ Available section constants: `SectionPreamble`, `SectionIdentity`, `SectionTone` `SectionIdentity` and `SectionToolInstructions` are section _groups_ that target a collection of related sub-sections as a unit. Use `SectionPreamble` to target just the identity preamble without affecting its sibling sub-sections. +`SectionLastInstructions` (`last_instructions`) includes configured subagent-model guidance when the `task` tool is available. Removing or replacing this section also removes that guidance; a `SectionOverride.Transform` callback receives the complete section, including the guidance, and its returned content is authoritative. Append, prepend, and preserve retain their usual section semantics. These overrides change prompt prose only, not configured subagent models, tool availability, or runtime dispatch policy. `SectionRuntimeInstructions` is a separate section: removing it does not remove `SectionLastInstructions`. + Each section override supports five actions: - **`replace`** — Replace the section content entirely @@ -601,6 +662,26 @@ editFile := copilot.DefineTool("edit_file", "Custom file editor with project-spe editFile.OverridesBuiltInTool = true ``` +An explicit `apply_patch` override can declare a root string schema by using a +`string` parameter with `DefineTool`. The model sees a required `input` property, +but the runtime restores the scalar patch text before dispatch. Both the typed +parameter and `ToolInvocation.Arguments` receive a string, not an +`{"input": ...}` object. This example returns trimmed patch text; replace the +handler body with your own patch implementation: + +```go +import "strings" + +applyPatch := copilot.DefineTool("apply_patch", "Apply a patch", + func(patch string, inv copilot.ToolInvocation) (string, error) { + return strings.TrimSpace(patch), nil + }) +applyPatch.OverridesBuiltInTool = true +``` + +String-schema `apply_patch` overrides cannot contain JSON Schema references; +use an object schema if references are needed. + #### Skipping Permission Prompts Set `SkipPermission = true` on a tool to allow it to execute without triggering a permission prompt: @@ -1056,6 +1137,22 @@ session, err := client.CreateSession(context.Background(), &copilot.SessionConfi return nil, nil }, + // Called before a subagent's first turn; context is prepended to its prompt + OnSubagentStart: func(input copilot.SubagentStartHookInput, invocation copilot.HookInvocation) (*copilot.SubagentStartHookOutput, error) { + fmt.Printf("Starting subagent %s\n", input.AgentName) + return &copilot.SubagentStartHookOutput{ + AdditionalContext: "Focus on the requested file.", + }, nil + }, + + // Called after a subagent finishes a turn + OnSubagentStop: func(input copilot.SubagentStopHookInput, invocation copilot.HookInvocation) (*copilot.SubagentStopHookOutput, error) { + fmt.Printf("Subagent %s finished: %s\n", input.AgentType, input.StopReason) + return &copilot.SubagentStopHookOutput{ + ModifiedResponse: copilot.String("Verified: " + input.Response), + }, nil + }, + // Called when an error occurs OnErrorOccurred: func(input copilot.ErrorOccurredHookInput, invocation copilot.HookInvocation) (*copilot.ErrorOccurredHookOutput, error) { fmt.Printf("Error in %s: %s\n", input.ErrorContext, input.Error) @@ -1075,8 +1172,13 @@ session, err := client.CreateSession(context.Background(), &copilot.SessionConfi - `OnUserPromptSubmitted` - Intercept user prompts. Can modify the prompt before processing. - `OnSessionStart` - Run logic when a session starts or resumes. - `OnSessionEnd` - Cleanup or logging when session ends. +- `OnSubagentStart` - Observe a subagent before its first turn and optionally prepend `AdditionalContext` to its prompt. +- `OnSubagentStop` - Observe a completed subagent turn. Return `Decision: "block"` with a non-empty `Reason` to request another turn, **or** `ModifiedResponse: copilot.String("replacement")` to replace its response (including an intentional empty string). +- `OnAgentStop` - Intercept the parent agent's stop; distinct from `OnSubagentStop`. - `OnErrorOccurred` - Handle errors with retry/skip/abort strategies. +Subagent hook inputs identify the **parent** session in `SessionID` and `HookInvocation.SessionID`. Both include a `time.Time` timestamp, `WorkingDirectory` (`cwd`), `TranscriptPath`, and `AgentName`. `AgentDisplayName` and `AgentDescription` may be empty. Stop inputs additionally include an optional `AgentID`, `AgentType`, `StopReason` (`"end_turn"`), and the subagent's `Response`. Registering either hook alone enables hook callbacks for both new and resumed sessions. + ## Commands Register slash-commands that users can invoke from the CLI TUI. When a user types `/deploy production`, the SDK dispatches to your handler and responds via the RPC layer. diff --git a/go/canvas.go b/go/canvas.go index e31598bb1e..0de02e4c38 100644 --- a/go/canvas.go +++ b/go/canvas.go @@ -112,6 +112,11 @@ func CanvasErrorNoHandler() *CanvasError { type CanvasHandler interface { OnOpen(ctx context.Context, c rpc.CanvasProviderOpenRequest) (rpc.CanvasProviderOpenResult, error) OnClose(ctx context.Context, c rpc.CanvasProviderCloseRequest) error + // OnAction handles a non-lifecycle action declared by the canvas. The + // returned value is sent to the model as the invoke_canvas_action tool + // result. To return text and images, as tool handlers do, return a + // ToolResult with BinaryResultsForLLM; any other value is rendered to the + // model as JSON text. OnAction(ctx context.Context, c rpc.CanvasProviderInvokeActionRequest) (any, error) } diff --git a/go/client.go b/go/client.go index 4e62e6530d..aa8659fd4b 100644 --- a/go/client.go +++ b/go/client.go @@ -42,6 +42,7 @@ import ( "os/exec" "path/filepath" "regexp" + "runtime/debug" "strconv" "strings" "sync" @@ -60,6 +61,12 @@ import ( // whole-session [ProviderConfig]. Named providers are keyed by their own Name. const defaultBearerTokenProviderName = "default" +type cloudSkillProviderError struct{} + +func (cloudSkillProviderError) Error() string { + return "Skill providers are not supported for cloud sessions." +} + // collectBearerTokenProviders gathers the per-provider [BearerTokenProvider] callbacks // from the singular provider and any named providers, keyed by provider name. The // singular provider uses the implicit name "default"; named providers use their @@ -543,8 +550,10 @@ func (c *Client) Start(ctx context.Context) error { } if c.options.SessionFS.Capabilities != nil { sqlite := c.options.SessionFS.Capabilities.Sqlite + binary := c.options.SessionFS.Capabilities.Binary req.Capabilities = &rpc.SessionFSSetProviderCapabilities{ Sqlite: &sqlite, + Binary: &binary, } } _, err := c.RPC.SessionFS.SetProvider(ctx, req) @@ -713,6 +722,14 @@ func (c *Client) logDebugTiming(start time.Time, message string) { // - Force closes the connection // - Kills the CLI process (if spawned by this client) // +// Event processing observes the stop signal before dispatching each remaining +// callback. Callbacks already being dispatched are not awaited and may finish +// after this method returns; an event handler can itself call ForceStop. +// Pending response waits fail when the session is retired, without waiting for +// an idle event or their context deadline. +// Registered session callbacks are released locally, including unadmitted event +// handler entries retained by a dispatch snapshot. +// // Example: // // // If normal stop hangs, force stop @@ -745,7 +762,9 @@ func (c *Client) ForceStop() { c.sessions = make(map[string]*Session) c.sessionsMux.Unlock() for _, session := range sessions { + session.clearSkillProvider() session.cancelPendingExternalTools() + session.stopEventProcessing() } c.clearGitHubTokenProviders() c.closeCopilotRequestAdapter() @@ -874,6 +893,9 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses if config == nil { config = &SessionConfig{} } + if config.Cloud != nil && config.SkillProvider != nil { + return nil, cloudSkillProviderError{} + } if config.GitHubToken != "" && config.GitHubTokenProvider != nil { return nil, fmt.Errorf("GitHubToken and GitHubTokenProvider cannot be used together") } @@ -911,6 +933,9 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.EnableHostGitOperations = config.EnableHostGitOperations req.EnableSessionStore = config.EnableSessionStore req.EnableSkills = config.EnableSkills + if config.SkillProvider != nil { + req.HasSkillProvider = Bool(true) + } req.Tools = config.Tools systemMessage := c.systemMessageForMode(config.SystemMessage) wireSystemMessage, transformCallbacks := extractTransformCallbacks(systemMessage) @@ -959,6 +984,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.InfiniteSessions = config.InfiniteSessions req.LargeOutput = config.LargeOutput req.ToolSearch = config.ToolSearch + req.ImageGeneration = config.ImageGeneration req.Memory = config.Memory req.GitHubToken = config.GitHubToken req.GitHubTokenProviderRegistrationID = registrationID @@ -976,6 +1002,7 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses req.FeatureFlags = &config.FeatureFlags } req.EnableManagedSettings = config.EnableManagedSettings + req.EnforceManagedModelDefaults = config.EnforceManagedModelDefaults req.ManagedSettings = config.ManagedSettings if len(config.Commands) > 0 { @@ -1022,7 +1049,9 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses config.Hooks.OnSessionStart != nil || config.Hooks.OnSessionEnd != nil || config.Hooks.OnErrorOccurred != nil || - config.Hooks.OnAgentStop != nil) { + config.Hooks.OnAgentStop != nil || + config.Hooks.OnSubagentStart != nil || + config.Hooks.OnSubagentStop != nil) { req.Hooks = Bool(true) } if config.OnPermissionRequest != nil { @@ -1108,6 +1137,9 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses if config.CanvasHandler != nil { s.registerCanvasHandler(config.CanvasHandler) } + if config.SkillProvider != nil { + s.registerSkillProvider(config.SkillProvider) + } if bearerTokenProviders := collectBearerTokenProviders(config.Provider, config.Providers); bearerTokenProviders != nil { s.registerBearerTokenProviders(bearerTokenProviders) } @@ -1124,6 +1156,12 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses return nil, fmt.Errorf("SessionFS capabilities declare SQLite support but the provider does not implement SessionFSSqliteProvider") } } + if c.options.SessionFS.Capabilities != nil && c.options.SessionFS.Capabilities.Binary { + if _, ok := provider.(SessionFSBinaryProvider); !ok { + unregisterSession(sessionID, s) + return nil, fmt.Errorf("SessionFS capabilities declare binary support but the provider does not implement SessionFSBinaryProvider") + } + } s.clientSessionAPIs.SessionFS = newSessionFSAdapter(provider) } @@ -1135,6 +1173,14 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses var session *Session var registeredSessionID string + var orphanedSessionID string + var inlineMu sync.Mutex + var requestFinished bool + deleteOrphan := func(sessionID string) error { + cleanupCtx, cancelCleanup := context.WithTimeout(context.WithoutCancel(ctx), 10*time.Second) + defer cancelCleanup() + return c.DeleteSession(cleanupCtx, sessionID) + } // Pre-register non-cloud sessions BEFORE issuing the RPC so any // session-scoped requests the CLI emits during session.create processing @@ -1169,19 +1215,48 @@ func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Ses return fmt.Errorf("session.create response did not include a sessionId") } s, err := initializeSession(early.SessionID) + inlineMu.Lock() if err != nil { + if requestFinished { + // Cleanup must not wait for an RPC response on the read loop. + go func() { + if cleanupErr := deleteOrphan(early.SessionID); cleanupErr != nil { + log.Printf("failed to delete uninitialized cloud session %s: %v", early.SessionID, cleanupErr) + } + }() + } else { + orphanedSessionID = early.SessionID + } + inlineMu.Unlock() return err } + if requestFinished { + inlineMu.Unlock() + unregisterSession(early.SessionID, s) + return nil + } session = s registeredSessionID = early.SessionID + inlineMu.Unlock() return nil } } result, err := c.client.RequestWithInlineResponse(ctx, "session.create", req, inlineCb) + inlineMu.Lock() + requestFinished = true + cleanupSessionID := orphanedSessionID + sessionToUnregister := session + registeredID := registeredSessionID + inlineMu.Unlock() if err != nil { - if registeredSessionID != "" { - unregisterSession(registeredSessionID, session) + if registeredID != "" { + unregisterSession(registeredID, sessionToUnregister) + } + if cleanupSessionID != "" { + if cleanupErr := deleteOrphan(cleanupSessionID); cleanupErr != nil { + err = errors.Join(err, fmt.Errorf("failed to delete uninitialized cloud session: %w", cleanupErr)) + } } return nil, fmt.Errorf("failed to create session: %w", err) } @@ -1347,7 +1422,9 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, config.Hooks.OnSessionStart != nil || config.Hooks.OnSessionEnd != nil || config.Hooks.OnErrorOccurred != nil || - config.Hooks.OnAgentStop != nil) { + config.Hooks.OnAgentStop != nil || + config.Hooks.OnSubagentStart != nil || + config.Hooks.OnSubagentStop != nil) { req.Hooks = Bool(true) } req.WorkingDirectory = config.WorkingDirectory @@ -1362,6 +1439,9 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.EnableHostGitOperations = config.EnableHostGitOperations req.EnableSessionStore = config.EnableSessionStore req.EnableSkills = config.EnableSkills + if config.SkillProvider != nil { + req.HasSkillProvider = Bool(true) + } if config.SuppressResumeEvent { req.DisableResume = Bool(true) } @@ -1385,6 +1465,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.InfiniteSessions = config.InfiniteSessions req.LargeOutput = config.LargeOutput req.ToolSearch = config.ToolSearch + req.ImageGeneration = config.ImageGeneration req.Memory = config.Memory req.GitHubToken = config.GitHubToken req.GitHubTokenProviderRegistrationID = registrationID @@ -1402,6 +1483,7 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, req.FeatureFlags = &config.FeatureFlags } req.EnableManagedSettings = config.EnableManagedSettings + req.EnforceManagedModelDefaults = config.EnforceManagedModelDefaults req.ManagedSettings = config.ManagedSettings if config.OnPermissionRequest != nil { req.RequestPermission = Bool(true) @@ -1471,6 +1553,9 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, if config.CanvasHandler != nil { session.registerCanvasHandler(config.CanvasHandler) } + if config.SkillProvider != nil { + session.registerSkillProvider(config.SkillProvider) + } if bearerTokenProviders := collectBearerTokenProviders(config.Provider, config.Providers); bearerTokenProviders != nil { session.registerBearerTokenProviders(bearerTokenProviders) } @@ -1487,6 +1572,12 @@ func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, return nil, fmt.Errorf("SessionFS capabilities declare SQLite support but the provider does not implement SessionFSSqliteProvider") } } + if c.options.SessionFS.Capabilities != nil && c.options.SessionFS.Capabilities.Binary { + if _, ok := provider.(SessionFSBinaryProvider); !ok { + session.stopEventProcessing() + return nil, fmt.Errorf("SessionFS capabilities declare binary support but the provider does not implement SessionFSBinaryProvider") + } + } session.clientSessionAPIs.SessionFS = newSessionFSAdapter(provider) } @@ -1688,6 +1779,7 @@ func (c *Client) DeleteSession(ctx context.Context, sessionID string) error { delete(c.sessions, sessionID) c.sessionsMux.Unlock() if session != nil { + session.clearSkillProvider() session.releaseGitHubTokenProviderRegistration() } @@ -2535,6 +2627,8 @@ func (c *Client) setupNotificationHandler() { c.client.SetRequestHandler("exitPlanMode.request", jsonrpc2.RequestHandlerFor(c.handleExitPlanModeRequest)) c.client.SetRequestHandler("autoModeSwitch.request", jsonrpc2.RequestHandlerFor(c.handleAutoModeSwitchRequest)) c.client.SetRequestHandler("systemMessage.transform", jsonrpc2.RequestHandlerFor(c.handleSystemMessageTransform)) + c.client.SetRequestContextHandler("skillProvider.list", c.handleSkillProviderList) + c.client.SetRequestContextHandler("skillProvider.read", c.handleSkillProviderRead) rpc.RegisterClientSessionAPIHandlers(c.client, func(sessionID string) *rpc.ClientSessionAPIHandlers { c.sessionsMux.Lock() defer c.sessionsMux.Unlock() @@ -2629,6 +2723,7 @@ func (c *Client) handleConnectionClose() { } c.sessionsMux.Unlock() for _, session := range sessions { + session.clearSkillProvider() session.cancelPendingExternalTools() } // Avoid deadlocking with Stop/ForceStop, which hold startStopMux while @@ -2746,6 +2841,102 @@ func (c *Client) handleSessionEvent(req sessionEventRequest) { } } +func (c *Client) handleSkillProviderList(ctx context.Context, params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + var req rpc.SkillProviderListRequest + if err := json.Unmarshal(params, &req); err != nil || req.SessionID == "" { + return nil, &jsonrpc2.Error{Code: -32602, Message: "invalid skill provider list payload"} + } + + provider, rpcErr := c.resolveSkillProvider(req.SessionID) + if rpcErr != nil { + return nil, rpcErr + } + + skills, err := callSkillProvider(ctx, req.SessionID, "listSkills", func() ([]rpc.SkillProviderDescriptor, error) { + return provider.ListSkills(ctx) + }) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: "Skill provider listSkills failed"} + } + if skills == nil { + skills = []rpc.SkillProviderDescriptor{} + } + + raw, err := json.Marshal(rpc.SkillProviderListResult{Skills: skills}) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("Failed to marshal response: %v", err)} + } + return raw, nil +} + +func (c *Client) handleSkillProviderRead(ctx context.Context, params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + var req rpc.SkillProviderReadRequest + if err := json.Unmarshal(params, &req); err != nil || req.SessionID == "" || req.Name == "" { + return nil, &jsonrpc2.Error{Code: -32602, Message: "invalid skill provider read payload"} + } + + provider, rpcErr := c.resolveSkillProvider(req.SessionID) + if rpcErr != nil { + return nil, rpcErr + } + + // Classify not-found inside the guarded call: errors.Is runs provider + // Is/Unwrap methods, which may panic. + markdown, err := callSkillProvider(ctx, req.SessionID, "readSkill", func() (*string, error) { + markdown, err := provider.ReadSkill(ctx, req.Name) + if errors.Is(err, ErrSkillNotFound) { + return nil, nil + } + if err != nil { + return nil, err + } + return &markdown, nil + }) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: "Skill provider readSkill failed"} + } + + raw, err := json.Marshal(rpc.SkillProviderReadResult{Markdown: markdown}) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("Failed to marshal response: %v", err)} + } + return raw, nil +} + +func (c *Client) resolveSkillProvider(sessionID string) (SkillProvider, *jsonrpc2.Error) { + c.sessionsMux.Lock() + session := c.sessions[sessionID] + c.sessionsMux.Unlock() + if session == nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("No skill provider for session: %s", sessionID)} + } + provider := session.getSkillProvider() + if provider == nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("No skill provider for session: %s", sessionID)} + } + return provider, nil +} + +var errSkillProviderPanic = errors.New("skill provider panicked") + +// callSkillProvider logs provider failures locally and converts panics into +// an error, so the runtime only ever receives a generic failure. +func callSkillProvider[T any](ctx context.Context, sessionID, operation string, call func() (T, error)) (result T, err error) { + defer func() { + if failure := recover(); failure != nil { + log.Printf("skill provider %s panicked: session_id=%s panic=%v\n%s", operation, sessionID, failure, debug.Stack()) + var zero T + result, err = zero, errSkillProviderPanic + } + }() + result, err = call() + // A call the runtime cancelled is expected to fail; it isn't a provider failure. + if err != nil && ctx.Err() == nil { + log.Printf("skill provider %s failed: session_id=%s error=%v", operation, sessionID, err) + } + return result, err +} + // handleUserInputRequest handles a user input request from the CLI server. func (c *Client) handleUserInputRequest(req userInputRequest) (*userInputResponse, *jsonrpc2.Error) { if req.SessionID == "" || req.Question == "" { diff --git a/go/client_test.go b/go/client_test.go index ea842aa5d8..c9a6225cde 100644 --- a/go/client_test.go +++ b/go/client_test.go @@ -3,8 +3,10 @@ package copilot import ( + "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net" @@ -543,9 +545,33 @@ func TestClient_ForceStopAndExternalStopDoNotRequestRuntimeShutdown(t *testing.T externalServer.Stop() } +func TestClient_ForceStopStopsRegisteredSessionEventProcessing(t *testing.T) { + rpcClient, server, shutdownCalled := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + sessions := make(map[string]*Session) + for _, id := range []string{"session-1", "session-2"} { + session := newSession(id, rpcClient, "", false) + t.Cleanup(session.stopEventProcessing) + sessions[id] = session + } + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + sessions: sessions, + } + + client.ForceStop() + for _, session := range sessions { + assertSessionEventChannelClosed(t, session) + } + client.ForceStop() + assertRuntimeShutdownNotCalled(t, shutdownCalled) +} + func TestClient_ForceStopCancelsPendingExternalTools(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) session := &Session{ + eventDone: make(chan struct{}), pendingExternalTools: map[string]*pendingExternalTool{ "request-1": {ctx: ctx, cancel: cancel}, }, @@ -565,6 +591,89 @@ func TestClient_ForceStopCancelsPendingExternalTools(t *testing.T) { } } +func TestClient_ForceStopDropsRemainingEventHandlers(t *testing.T) { + for _, fromHandler := range []bool{false, true} { + name := "while handler is blocked" + if fromHandler { + name = "from event handler" + } + t.Run(name, func(t *testing.T) { + session := &Session{ + eventCh: make(chan SessionEvent, 1), + eventDone: make(chan struct{}), + } + client := &Client{sessions: map[string]*Session{"session-1": session}} + entered := make(chan struct{}) + release := make(chan struct{}) + releaseHandler := sync.OnceFunc(func() { close(release) }) + forceStopped := make(chan struct{}) + consumerExited := make(chan struct{}) + unexpected := make(chan string, 3) + t.Cleanup(func() { + session.stopEventProcessing() + releaseHandler() + select { + case <-consumerExited: + case <-time.After(2 * time.Second): + t.Error("event consumer did not exit during cleanup") + } + }) + session.On(func(event SessionEvent) { + if _, ok := event.Data.(*SessionIdleData); !ok { + unexpected <- "queued event" + return + } + close(entered) + <-release + if fromHandler { + client.ForceStop() + close(forceStopped) + } + }) + session.On(func(SessionEvent) { unexpected <- "remaining handler" }) + go func() { + defer close(consumerExited) + session.processEvents() + }() + + session.eventCh <- newTestEvent() + select { + case <-entered: + case <-time.After(2 * time.Second): + t.Fatal("event handler did not enter its barrier") + } + session.eventCh <- SessionEvent{Data: &AssistantMessageData{ + Content: "queued", + MessageID: "queued", + }} + if fromHandler { + releaseHandler() + } else { + go func() { + client.ForceStop() + close(forceStopped) + }() + } + select { + case <-forceStopped: + case <-time.After(2 * time.Second): + t.Fatal("ForceStop waited for the in-flight event handler") + } + releaseHandler() + select { + case <-consumerExited: + case <-time.After(2 * time.Second): + t.Fatal("event consumer did not exit after the handler returned") + } + select { + case callback := <-unexpected: + t.Fatalf("ForceStop dispatched %s after cancellation", callback) + default: + } + }) + } +} + func TestClient_ConnectionCloseCancelsPendingExternalTools(t *testing.T) { rpcClient, server, _ := newRuntimeShutdownRpcPair(t) server.SetRequestHandler("session.detach", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { @@ -807,6 +916,76 @@ func TestClient_ForwardsAskUserVariantToSessionRequests(t *testing.T) { assertAskUserVariant(t, <-resumeParams, "") } +func TestClient_EnablesSubagentHooksForCreateAndResume(t *testing.T) { + for _, tt := range []struct { + name string + hooks *SessionHooks + }{ + { + name: "start only", + hooks: &SessionHooks{ + OnSubagentStart: func(SubagentStartHookInput, HookInvocation) (*SubagentStartHookOutput, error) { + return nil, nil + }, + }, + }, + { + name: "stop only", + hooks: &SessionHooks{ + OnSubagentStop: func(SubagentStopHookInput, HookInvocation) (*SubagentStopHookOutput, error) { + return nil, nil + }, + }, + }, + } { + 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), + } + 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...) + sessionID := sessionIDFromParams(t, params) + return []byte(`{"sessionId":"` + sessionID + `"}`), nil + }) + } + server.SetRequestHandler("session.detach", func(json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + return []byte(`{"success":true}`), nil + }) + + created, err := client.CreateSession(t.Context(), &SessionConfig{Hooks: tt.hooks}) + if err != nil { + t.Fatalf("create session: %v", err) + } + if err := created.Disconnect(); err != nil { + t.Fatalf("disconnect session: %v", err) + } + resumed, err := client.ResumeSession(t.Context(), created.SessionID, &ResumeSessionConfig{Hooks: tt.hooks}) + if err != nil { + t.Fatalf("resume session: %v", err) + } + if err := resumed.Disconnect(); err != nil { + t.Fatalf("disconnect resumed session: %v", err) + } + + for _, method := range []string{"session.create", "session.resume"} { + var request map[string]any + if err := json.Unmarshal(<-requests, &request); err != nil { + t.Fatalf("decode %s request: %v", method, err) + } + if request["hooks"] != true { + t.Errorf("%s hooks = %v, want true", method, request["hooks"]) + } + } + }) + } +} + func TestClient_ForwardsDiagnosticsToSessionRequests(t *testing.T) { rpcClient, server, _ := newRuntimeShutdownRpcPair(t) t.Cleanup(server.Stop) @@ -1449,6 +1628,63 @@ func (noSQLiteSessionFSProvider) ReadDirectoryWithTypes(string) ([]rpc.SessionFS func (noSQLiteSessionFSProvider) Remove(string, bool, bool) error { return nil } func (noSQLiteSessionFSProvider) Rename(string, string) error { return nil } +type oversizedBinarySessionFSProvider struct{ noSQLiteSessionFSProvider } + +func (oversizedBinarySessionFSProvider) ReadFileBytes(string) ([]byte, error) { + return make([]byte, maxSessionFSBinaryBytes+1), nil +} + +func (oversizedBinarySessionFSProvider) WriteFileBytes(string, []byte, *int) error { return nil } + +type recordingBinarySessionFSProvider struct { + noSQLiteSessionFSProvider + content []byte + mode *int +} + +func (p *recordingBinarySessionFSProvider) ReadFileBytes(string) ([]byte, error) { + return p.content, nil +} +func (p *recordingBinarySessionFSProvider) WriteFileBytes(_ string, content []byte, mode *int) error { + p.content, p.mode = content, mode + return nil +} + +func TestSessionFSAdapterWritesExactBytesAndRejectsInvalidInput(t *testing.T) { + provider := &recordingBinarySessionFSProvider{} + adapter := newSessionFSAdapter(provider) + mode := int64(0o600) + write := &rpc.SessionFSWriteFileBytesRequest{ + SessionID: "session", Path: "/image.png", Content: "AP/+AQ==", Mode: &mode, + } + result, err := adapter.WriteFileBytes(write) + if err != nil || result != nil || !bytes.Equal(provider.content, []byte{0, 255, 254, 1}) || + provider.mode == nil || *provider.mode != 0o600 { + t.Fatalf("binary write lost content/mode: result=%v err=%v bytes=%v mode=%v", result, err, provider.content, provider.mode) + } + for _, content := range []string{"AA==AAAA", "AA==\r\n", strings.Repeat("A", maxSessionFSBinaryContentLength+1)} { + result, err := adapter.WriteFileBytes(&rpc.SessionFSWriteFileBytesRequest{ + SessionID: "session", Path: "/image.png", Content: content, + }) + if err != nil || result == nil || !bytes.Equal(provider.content, []byte{0, 255, 254, 1}) { + t.Fatalf("invalid binary write modified file: result=%v err=%v bytes=%v", result, err, provider.content) + } + } +} + +func TestSessionFSAdapterRejectsOversizedBinaryRead(t *testing.T) { + result, err := newSessionFSAdapter(oversizedBinarySessionFSProvider{}).ReadFileBytes( + &rpc.SessionFSReadFileBytesRequest{SessionID: "session", Path: "/image.png"}, + ) + if err != nil { + t.Fatalf("ReadFileBytes returned a transport error: %v", err) + } + if result.Content != "" || result.Error == nil || result.Error.Code != rpc.SessionFSErrorCodeUNKNOWN || + result.Error.Message == nil || !strings.Contains(*result.Error.Message, "binary read limit") { + t.Fatalf("expected a filesystem error without encoded bytes, got %+v", result) + } +} + func assertRuntimeShutdownNotCalled(t *testing.T, shutdownCalled <-chan struct{}) { t.Helper() select { @@ -1500,6 +1736,113 @@ func TestClient_SessionFSConfig(t *testing.T) { }) } +func TestClient_CreateSessionDeletesCloudSessionWhenBinaryProviderValidationFails(t *testing.T) { + client, requests, cleanup := newInMemoryClientWithOptions(t, &ClientOptions{ + SessionFS: &SessionFSConfig{ + InitialWorkingDirectory: "/", + SessionStatePath: "/state", + Conventions: rpc.SessionFSSetProviderConventionsPosix, + Capabilities: &SessionFSCapabilities{Binary: true}, + }, + }) + defer cleanup() + + _, err := client.CreateSession(t.Context(), &SessionConfig{ + Cloud: &CloudSessionOptions{ + Repository: &CloudSessionRepository{Owner: "github", Name: "copilot-sdk", Branch: "main"}, + }, + OnPermissionRequest: PermissionHandler.ApproveAll, + CreateSessionFSProvider: func(*Session) SessionFSProvider { + return nil + }, + }) + if err == nil || !strings.Contains(err.Error(), "does not implement SessionFSBinaryProvider") { + t.Fatalf("expected binary provider validation error, got %v", err) + } + snapshot := requests.snapshot() + if len(snapshot) != 2 || snapshot[0].Method != "session.create" || snapshot[1].Method != "session.delete" { + t.Fatalf("expected create then orphan cleanup, got %+v", snapshot) + } + if snapshot[1].Params["sessionId"] != "server-assigned-session-1" { + t.Fatalf("expected cleanup of server-assigned session, got %+v", snapshot[1]) + } + client.sessionsMux.Lock() + defer client.sessionsMux.Unlock() + if len(client.sessions) != 0 { + t.Fatalf("failed creation left %d active sessions", len(client.sessions)) + } +} + +func TestClient_CreateSessionDeletesCloudSessionWhenProviderValidationFailsAfterCancellation(t *testing.T) { + client, requests, cleanup := newInMemoryClientWithOptions(t, &ClientOptions{ + SessionFS: &SessionFSConfig{ + InitialWorkingDirectory: "/", + SessionStatePath: "/state", + Conventions: rpc.SessionFSSetProviderConventionsPosix, + Capabilities: &SessionFSCapabilities{Binary: true}, + }, + }) + defer cleanup() + requests.notify = make(chan struct{}, 1) + + providerStarted := make(chan struct{}) + releaseProvider := make(chan struct{}) + release := sync.OnceFunc(func() { close(releaseProvider) }) + defer release() + + ctx, cancel := context.WithCancel(t.Context()) + defer cancel() + createDone := make(chan error, 1) + go func() { + _, err := client.CreateSession(ctx, &SessionConfig{ + Cloud: &CloudSessionOptions{Repository: &CloudSessionRepository{Owner: "github", Name: "copilot-sdk", Branch: "main"}}, + OnPermissionRequest: PermissionHandler.ApproveAll, + CreateSessionFSProvider: func(*Session) SessionFSProvider { + close(providerStarted) + <-releaseProvider + return noSQLiteSessionFSProvider{} + }, + }) + createDone <- err + }() + + deadline := time.NewTimer(5 * time.Second) + defer deadline.Stop() + select { + case <-providerStarted: + case <-deadline.C: + t.Fatal("session.create did not reach the provider factory") + } + cancel() + select { + case err := <-createDone: + if !errors.Is(err, context.Canceled) { + t.Fatalf("CreateSession error = %v, want context cancellation", err) + } + case <-deadline.C: + t.Fatal("CreateSession did not return after cancellation") + } + release() + + for { + snapshot := requests.snapshot() + if len(snapshot) >= 2 { + if len(snapshot) != 2 || snapshot[0].Method != "session.create" || snapshot[1].Method != "session.delete" { + t.Fatalf("expected one create and one orphan cleanup, got %+v", snapshot) + } + if snapshot[1].Params["sessionId"] != "server-assigned-session-1" { + t.Fatalf("expected cleanup of server-assigned session, got %+v", snapshot[1]) + } + return + } + select { + case <-requests.notify: + case <-deadline.C: + t.Fatalf("cloud session was not deleted after provider validation failed; requests: %+v", snapshot) + } + } +} + func TestClient_AuthOptions(t *testing.T) { t.Run("should accept GitHubToken option", func(t *testing.T) { client := NewClient(&ClientOptions{ @@ -3138,12 +3481,19 @@ type recordedRequest struct { type requestRecorder struct { mu sync.Mutex requests []recordedRequest + notify chan struct{} } func (r *requestRecorder) append(request recordedRequest) { r.mu.Lock() defer r.mu.Unlock() r.requests = append(r.requests, request) + if r.notify != nil { + select { + case r.notify <- struct{}{}: + default: + } + } } func (r *requestRecorder) snapshot() []recordedRequest { @@ -3232,6 +3582,8 @@ func serveInMemoryRuntime(t *testing.T, stdinR *io.PipeReader, stdoutW *io.PipeW result = map[string]any{} case "session.detach": result = map[string]any{"success": true} + case "session.delete": + result = map[string]any{"success": true} default: t.Errorf("unexpected JSON-RPC method %s", request.Method) return @@ -4942,18 +5294,22 @@ func TestIsTerminal(t *testing.T) { func TestSessionRequests_ManagedSettings(t *testing.T) { settings := &ManagedSettings{ Permissions: &ManagedSettingsPermissions{ - DisableBypassPermissionsMode: DisableBypassPermissionsModeDisable, - Deny: []string{"Shell(git push)"}, - Ask: []string{"Domain(publish.example)"}, - Allow: []string{"Read(**)"}, + DisableBypassPermissionsMode: DisableBypassPermissionsModeDisable, + DisableAssistedPermissionsMode: Bool(true), + Deny: []string{"Shell(git push)"}, + Ask: []string{"Domain(publish.example)"}, + Allow: []string{"Read(**)"}, + LimitTo: []string{"Domain(github.com)"}, }, } expectedPermissions := map[string]any{ - "disableBypassPermissionsMode": "disable", - "deny": []any{"Shell(git push)"}, - "ask": []any{"Domain(publish.example)"}, - "allow": []any{"Read(**)"}, + "disableBypassPermissionsMode": "disable", + "disableAssistedPermissionsMode": true, + "deny": []any{"Shell(git push)"}, + "ask": []any{"Domain(publish.example)"}, + "allow": []any{"Read(**)"}, + "limitTo": []any{"Domain(github.com)"}, } t.Run("direct injection enables managed safeguards", func(t *testing.T) { @@ -4966,7 +5322,11 @@ func TestSessionRequests_ManagedSettings(t *testing.T) { }) t.Run("includes managedSettings on create when set", func(t *testing.T) { - req := createSessionRequest{EnableManagedSettings: Bool(true), ManagedSettings: settings} + req := createSessionRequest{ + EnableManagedSettings: Bool(true), + EnforceManagedModelDefaults: Bool(true), + ManagedSettings: settings, + } data, err := json.Marshal(req) if err != nil { t.Fatalf("Failed to marshal: %v", err) @@ -4978,6 +5338,9 @@ func TestSessionRequests_ManagedSettings(t *testing.T) { if m["enableManagedSettings"] != true { t.Errorf("Expected enableManagedSettings true, got %v", m["enableManagedSettings"]) } + if m["enforceManagedModelDefaults"] != true { + t.Errorf("Expected enforceManagedModelDefaults true, got %v", m["enforceManagedModelDefaults"]) + } ms, ok := m["managedSettings"].(map[string]any) if !ok { t.Fatalf("Expected managedSettings object, got %v", m["managedSettings"]) @@ -5045,6 +5408,7 @@ func TestSessionRequests_ManagedSettings(t *testing.T) { Deny: []string{}, Ask: []string{}, Allow: []string{}, + LimitTo: []string{}, }, }} data, err := json.Marshal(req) @@ -5057,7 +5421,7 @@ func TestSessionRequests_ManagedSettings(t *testing.T) { if perms["disableBypassPermissionsMode"] != "disable" { t.Errorf("Expected disableBypassPermissionsMode preserved, got %v", perms["disableBypassPermissionsMode"]) } - for _, key := range []string{"deny", "ask", "allow"} { + for _, key := range []string{"deny", "ask", "allow", "limitTo"} { if value, ok := perms[key].([]any); !ok || len(value) != 0 { t.Errorf("Expected %s to be an explicit empty array, got %v", key, perms[key]) } diff --git a/go/cmd/bundler/main.go b/go/cmd/bundler/main.go index 8c088eb488..f7a34413d1 100644 --- a/go/cmd/bundler/main.go +++ b/go/cmd/bundler/main.go @@ -541,7 +541,7 @@ func runtimeWrapperName(binaryName string) string { } var hostlessExcludedTopLevel = map[string]bool{ - "app.js": true, "assets": true, "changelog.json": true, "copilot": true, "copilot.exe": true, + "app.js": true, "assets": true, "changelog.json": true, "cli-main.js": true, "copilot": true, "copilot.exe": true, "foundry-local-sdk": true, "index.js": true, "napi-oop-runtime": true, "LICENSE.md": true, "npm-loader.js": true, "package.json": true, "pvrecorder": true, "queries": true, "README.md": true, "sea-loader.js": true, "webview": true, diff --git a/go/cmd/bundler/main_test.go b/go/cmd/bundler/main_test.go index 7325489767..90bdea1689 100644 --- a/go/cmd/bundler/main_test.go +++ b/go/cmd/bundler/main_test.go @@ -30,6 +30,7 @@ func TestCreateRuntimeAssetsArchiveRetainsUnknownAssetsAndFiltersCLIContent(t *t "package/preloads/extension_bootstrap.mjs": "preload", "package/sdk/factory.js": "factory", "package/app.js": "excluded", + "package/cli-main.js": "excluded", "package/LICENSE.md": "excluded", "package/README.md": "excluded", }) @@ -50,7 +51,7 @@ func TestCreateRuntimeAssetsArchiveRetainsUnknownAssetsAndFiltersCLIContent(t *t t.Fatalf("retained assets = %#v", files) } for _, excluded := range []string{ - "runtime.node", "copilot-runtime", "app.js", "LICENSE.md", "README.md", + "runtime.node", "copilot-runtime", "app.js", "cli-main.js", "LICENSE.md", "README.md", } { if _, ok := files[excluded]; ok { t.Fatalf("excluded asset %q was retained", excluded) diff --git a/go/force_stop_test.go b/go/force_stop_test.go new file mode 100644 index 0000000000..89a5f6c874 --- /dev/null +++ b/go/force_stop_test.go @@ -0,0 +1,412 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package copilot_test + +import ( + "bufio" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net" + "runtime" + "strconv" + "strings" + "sync" + "testing" + "time" + + copilot "github.com/github/copilot-sdk/go" +) + +type responseRetirementPeer struct { + listener net.Listener + mu sync.Mutex + conn net.Conn + writeMu sync.Mutex + sent chan struct{} + done chan error +} + +func newResponseRetirementPeer(t *testing.T, expectedSends int) *responseRetirementPeer { + t.Helper() + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + peer := &responseRetirementPeer{ + listener: listener, + sent: make(chan struct{}), + done: make(chan error, 1), + } + t.Cleanup(func() { + if err := listener.Close(); err != nil && !errors.Is(err, net.ErrClosed) { + t.Error("close peer listener:", err) + } + peer.mu.Lock() + conn := peer.conn + peer.mu.Unlock() + if conn != nil { + if err := conn.Close(); err != nil && !errors.Is(err, net.ErrClosed) { + t.Error("close peer connection:", err) + } + } + select { + case err := <-peer.done: + if err != nil { + t.Error("TCP peer:", err) + } + case <-time.After(5 * time.Second): + t.Error("TCP peer did not exit") + } + }) + go func() { peer.done <- peer.serve(expectedSends) }() + return peer +} + +func (p *responseRetirementPeer) serve(expectedSends int) error { + conn, err := p.listener.Accept() + if err != nil { + if errors.Is(err, net.ErrClosed) { + return nil + } + return err + } + p.mu.Lock() + p.conn = conn + p.mu.Unlock() + defer conn.Close() + reader := bufio.NewReader(conn) + sends := 0 + for { + frame, err := readRetirementFrame(reader) + if err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, net.ErrClosed) { + return nil + } + return err + } + var request struct { + ID json.RawMessage `json:"id"` + Method string `json:"method"` + } + if err := json.Unmarshal(frame, &request); err != nil { + return err + } + var result any + switch request.Method { + case "connect": + result = map[string]any{"ok": true, "protocolVersion": copilot.SDKProtocolVersion, "version": "force-stop-test"} + case "session.create": + result = map[string]any{"sessionId": "retirement-session"} + case "session.send": + sends++ + result = map[string]any{"messageId": fmt.Sprintf("message-%d", sends)} + case "ping": + result = map[string]any{"message": "pong", "timestamp": "2026-01-01T00:00:00Z", "protocolVersion": copilot.SDKProtocolVersion} + default: + return fmt.Errorf("unexpected RPC: %s", request.Method) + } + if err := p.write(map[string]any{"jsonrpc": "2.0", "id": request.ID, "result": result}); err != nil { + return err + } + if request.Method == "session.send" && sends == expectedSends { + close(p.sent) + } + } +} + +func readRetirementFrame(reader *bufio.Reader) ([]byte, error) { + length := -1 + for { + line, err := reader.ReadString('\n') + if err != nil { + if errors.Is(err, io.EOF) && line == "" && length == -1 { + return nil, io.EOF + } + return nil, fmt.Errorf("read frame header: %w", err) + } + if line == "\r\n" { + break + } + name, value, ok := strings.Cut(line, ":") + if !ok { + return nil, fmt.Errorf("invalid frame header: %q", line) + } + if strings.EqualFold(name, "Content-Length") { + if length != -1 { + return nil, errors.New("duplicate Content-Length") + } + length, err = strconv.Atoi(strings.TrimSpace(value)) + if err != nil { + return nil, err + } + } + } + if length <= 0 || length > 128*1024 { + return nil, fmt.Errorf("invalid frame length: %d", length) + } + body := make([]byte, length) + if _, err := io.ReadFull(reader, body); err != nil { + return nil, err + } + return body, nil +} + +func (p *responseRetirementPeer) write(message any) error { + body, err := json.Marshal(message) + if err != nil { + return err + } + p.writeMu.Lock() + defer p.writeMu.Unlock() + p.mu.Lock() + conn := p.conn + p.mu.Unlock() + _, err = fmt.Fprintf(conn, "Content-Length: %d\r\n\r\n%s", len(body), body) + return err +} + +type responseRetirementCapture struct { + data [64]byte +} + +func capturedRetirementHandler() (copilot.SessionEventHandler, <-chan struct{}) { + capture := &responseRetirementCapture{data: [64]byte{1}} + collected := make(chan struct{}) + runtime.AddCleanup(capture, func(done chan struct{}) { close(done) }, collected) + return func(copilot.SessionEvent) { runtime.KeepAlive(capture) }, collected +} + +func awaitRetirementCapture(t *testing.T, collected <-chan struct{}) { + t.Helper() + deadline := time.Now().Add(5 * time.Second) + for { + runtime.GC() + select { + case <-collected: + return + default: + } + if time.Now().After(deadline) { + t.Fatal("force-stopped retained session still roots an unadmitted callback capture") + } + runtime.Gosched() + } +} + +func TestForceStopReleasesRetainedEventCallbacks(t *testing.T) { + for _, blockedHandler := range []bool{false, true} { + t.Run(fmt.Sprintf("blocked handler=%t", blockedHandler), func(t *testing.T) { + t.Parallel() + peer := newResponseRetirementPeer(t, 0) + client := copilot.NewClient(&copilot.ClientOptions{ + Connection: copilot.URIConnection{URL: peer.listener.Addr().String()}, + }) + t.Cleanup(client.ForceStop) + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) + defer cancel() + if err := client.Start(ctx); err != nil { + t.Fatal("start:", err) + } + session, err := client.CreateSession(ctx, &copilot.SessionConfig{ + SessionID: "retirement-session", OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + }) + if err != nil { + t.Fatal("create:", err) + } + entered := make(chan struct{}) + release := make(chan struct{}) + exited := make(chan struct{}) + admitted := false + releaseHandler := sync.OnceFunc(func() { close(release) }) + t.Cleanup(func() { + client.ForceStop() + releaseHandler() + if admitted { + select { + case <-exited: + case <-time.After(5 * time.Second): + t.Error("admitted event handler did not finish") + } + } + }) + if blockedHandler { + session.On(func(copilot.SessionEvent) { + close(entered) + <-release + close(exited) + }) + } + handler, collected := capturedRetirementHandler() + unsubscribe := session.On(handler) + if blockedHandler { + if err := peer.write(map[string]any{ + "jsonrpc": "2.0", "method": "session.event", + "params": map[string]any{ + "sessionId": session.SessionID, + "event": map[string]any{ + "id": "00000000-0000-4000-8000-000000000002", "parentId": nil, + "timestamp": "2026-01-01T00:00:00Z", "type": "session.idle", + "ephemeral": true, "data": map[string]any{}, + }, + }, + }); err != nil { + t.Fatal("notify:", err) + } + select { + case <-entered: + admitted = true + case <-ctx.Done(): + t.Fatal("event handler did not enter:", ctx.Err()) + } + } + client.ForceStop() + awaitRetirementCapture(t, collected) + runtime.KeepAlive(session) + runtime.KeepAlive(unsubscribe) + }) + } +} + +func createRetirementHookSession(t *testing.T, ctx context.Context, client *copilot.Client) (*copilot.Session, <-chan struct{}) { + t.Helper() + capture := &responseRetirementCapture{data: [64]byte{2}} + collected := make(chan struct{}) + runtime.AddCleanup(capture, func(done chan struct{}) { close(done) }, collected) + session, err := client.CreateSession(ctx, &copilot.SessionConfig{ + SessionID: "retirement-session", OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Hooks: &copilot.SessionHooks{ + OnSessionEnd: func(copilot.SessionEndHookInput, copilot.HookInvocation) (*copilot.SessionEndHookOutput, error) { + runtime.KeepAlive(capture) + return nil, nil + }, + }, + }) + if err != nil { + t.Fatal("create:", err) + } + return session, collected +} + +func TestForceStopReleasesRetainedHookCallbacks(t *testing.T) { + peer := newResponseRetirementPeer(t, 0) + client := copilot.NewClient(&copilot.ClientOptions{ + Connection: copilot.URIConnection{URL: peer.listener.Addr().String()}, + }) + t.Cleanup(client.ForceStop) + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) + defer cancel() + if err := client.Start(ctx); err != nil { + t.Fatal("start:", err) + } + session, collected := createRetirementHookSession(t, ctx, client) + client.ForceStop() + awaitRetirementCapture(t, collected) + runtime.KeepAlive(session) +} + +func TestForceStopRejectsAcknowledgedPlainResponseWaits(t *testing.T) { + for _, count := range []int{1, 2} { + for _, blockedHandler := range []bool{false, true} { + t.Run(fmt.Sprintf("%d waits/blocked handler=%t", count, blockedHandler), func(t *testing.T) { + t.Parallel() + peer := newResponseRetirementPeer(t, count) + client := copilot.NewClient(&copilot.ClientOptions{ + Connection: copilot.URIConnection{URL: peer.listener.Addr().String()}, + }) + t.Cleanup(client.ForceStop) + operationCtx, cancelOperation := context.WithTimeout(t.Context(), 10*time.Second) + defer cancelOperation() + if err := client.Start(operationCtx); err != nil { + t.Fatal("start:", err) + } + + session, err := client.CreateSession(operationCtx, &copilot.SessionConfig{ + SessionID: "retirement-session", OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + }) + if err != nil { + t.Fatal("create session:", err) + } + + entered := make(chan struct{}) + release := make(chan struct{}) + releaseHandler := sync.OnceFunc(func() { close(release) }) + t.Cleanup(releaseHandler) + if blockedHandler { + session.On(func(copilot.SessionEvent) { + close(entered) + <-release + }) + } + waitCtx, cancelWait := context.WithCancel(t.Context()) + results := make(chan error, count) + var waits sync.WaitGroup + t.Cleanup(func() { + cancelWait() + releaseHandler() + waits.Wait() + }) + for range count { + waits.Add(1) + go func() { + defer waits.Done() + _, err := session.SendAndWait(waitCtx, copilot.MessageOptions{Prompt: "hold the response"}) + results <- err + }() + } + select { + case <-peer.sent: + case <-operationCtx.Done(): + t.Fatal("send acknowledgments:", operationCtx.Err()) + } + // This later reply proves every preceding send acknowledgment reached the transport. + if _, err := client.Ping(operationCtx, "barrier"); err != nil { + t.Fatal("ping:", err) + } + if blockedHandler { + err := peer.write(map[string]any{ + "jsonrpc": "2.0", "method": "session.event", + "params": map[string]any{ + "sessionId": session.SessionID, + "event": map[string]any{ + "id": "00000000-0000-4000-8000-000000000001", "parentId": nil, + "timestamp": "2026-01-01T00:00:00Z", "type": "session.idle", + "ephemeral": true, "data": map[string]any{}, + }, + }, + }) + if err != nil { + t.Fatal("notify:", err) + } + select { + case <-entered: + case <-operationCtx.Done(): + t.Fatal("event handler did not enter:", operationCtx.Err()) + } + } + stopped := make(chan struct{}) + go func() { + client.ForceStop() + close(stopped) + }() + select { + case <-stopped: + case <-operationCtx.Done(): + t.Fatal("force stop joined the admitted handler:", operationCtx.Err()) + } + for range count { + select { + case err := <-results: + if err == nil || !strings.Contains(err.Error(), "session closed before response completed") { + t.Fatalf("expected response retirement error, got %v", err) + } + case <-time.After(5 * time.Second): + t.Fatal("acknowledged response wait remained blocked after force stop") + } + } + }) + } + } +} diff --git a/go/host.go b/go/host.go index 0abdd5dc15..7a75d0d7fb 100644 --- a/go/host.go +++ b/go/host.go @@ -41,6 +41,9 @@ type AhpSessionResumeRequest struct { // At least one transport must be supplied. Both may be enabled. // Experimental: AHP hosting may change or be removed. type AhpHostOptions struct { + // ComputeID selects a durable catalog identity; defaults are runtime-owned. + // It must agree with GitHubEnvironment.ComputeID when both are supplied. + ComputeID *string // LocalServer explicitly enables the local WebSocket listener. LocalServer *rpc.HostLocalServerOptions // GitHubEnvironment registers the host with Mission Control; Name and ComputeID are required. @@ -73,12 +76,17 @@ func (h *AhpHost) Dispose(ctx context.Context) error { return err } -// PublishSession exposes the exact resident session attached to the owning client. +// PublishSession durably advertises the exact attached session in the compute-scoped catalog. // It does not invoke factories or transfer ownership. func (h *AhpHost) PublishSession(ctx context.Context, sessionID string) (*rpc.HostPublishSessionResult, error) { return h.rpc.PublishSession(ctx, &rpc.HostPublishSessionRequest{HostID: h.HostID, SessionID: sessionID}) } +// ListSessions returns all live and dormant catalog sessions advertised by this host. +func (h *AhpHost) ListSessions(ctx context.Context) (*rpc.HostListSessionsResult, error) { + return h.rpc.ListSessions(ctx, &rpc.HostListSessionsRequest{HostID: &h.HostID}) +} + type ahpHostState struct { mu sync.Mutex hosts map[string]AhpHostOptions @@ -129,7 +137,7 @@ func (c *Client) StartAhpHost(ctx context.Context, options *AhpHostOptions) (*Ah c.ahp.hosts[hostID] = opts c.ahp.mu.Unlock() params := &rpc.HostStartRequest{ - HostID: hostID, LocalServer: opts.LocalServer, + HostID: hostID, ComputeID: opts.ComputeID, LocalServer: opts.LocalServer, GitHubEnvironment: opts.GitHubEnvironment, } if opts.CreateSession != nil { diff --git a/go/host_test.go b/go/host_test.go index c660b9b3ea..a6450f0904 100644 --- a/go/host_test.go +++ b/go/host_test.go @@ -47,8 +47,8 @@ func TestAhpHostTransportsAndOptionalResults(t *testing.T) { for _, transport := range []string{"local", "github", "both"} { t.Run(transport, func(t *testing.T) { client, server, _ := ahpFixture(t) - options := &AhpHostOptions{} - want := map[string]any{} + options := &AhpHostOptions{ComputeID: String("compute")} + want := map[string]any{"computeId": "compute"} if transport != "github" { port := int32(0) options.LocalServer = &rpc.HostLocalServerOptions{ @@ -111,6 +111,27 @@ func TestAhpHostTransportsAndOptionalResults(t *testing.T) { } } +func TestAhpHostListSessionsUsesOwningHost(t *testing.T) { + client, server, _ := ahpFixture(t) + host, err := client.StartAhpHost(t.Context(), &AhpHostOptions{LocalServer: &rpc.HostLocalServerOptions{}}) + if err != nil { + t.Fatal(err) + } + server.SetRequestHandler("host.listSessions", jsonrpc2.RequestHandlerFor(func(params *rpc.HostListSessionsRequest) (*rpc.HostListSessionsResult, *jsonrpc2.Error) { + if params.HostID == nil || *params.HostID != host.HostID { + t.Errorf("hostId = %v, want %q", params.HostID, host.HostID) + } + return &rpc.HostListSessionsResult{Sessions: []rpc.HostSessionSummary{}}, nil + })) + result, err := host.ListSessions(t.Context()) + if err != nil { + t.Fatal(err) + } + if result.Sessions == nil || len(result.Sessions) != 0 { + t.Fatalf("sessions = %#v, want an empty catalog", result.Sessions) + } +} + func TestAhpFactoryCancellationLogging(t *testing.T) { output := new(ahpTestLog) previous := log.Writer() diff --git a/go/image_generation_test.go b/go/image_generation_test.go new file mode 100644 index 0000000000..1c6586692c --- /dev/null +++ b/go/image_generation_test.go @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package copilot + +import ( + "encoding/json" + "testing" +) + +func TestImageGenerationSerialization(t *testing.T) { + for _, enabled := range []*bool{Bool(true), Bool(false), nil} { + var config *ImageGenerationConfig + if enabled != nil { + config = &ImageGenerationConfig{Enabled: enabled} + } + for _, request := range []any{ + createSessionRequest{ImageGeneration: config}, + resumeSessionRequest{SessionID: "image-test", ImageGeneration: config}, + } { + data, err := json.Marshal(request) + if err != nil { + t.Fatal(err) + } + var payload map[string]json.RawMessage + if err := json.Unmarshal(data, &payload); err != nil { + t.Fatal(err) + } + value, present := payload["imageGeneration"] + if enabled == nil { + if present { + t.Fatalf("expected omitted imageGeneration, got %s", data) + } + } else { + expected, err := json.Marshal(map[string]bool{"enabled": *enabled}) + if err != nil { + t.Fatal(err) + } + if string(value) != string(expected) { + t.Fatalf("imageGeneration = %s, want %s", value, expected) + } + } + } + } + data, err := json.Marshal(ImageGenerationConfig{}) + if err != nil || string(data) != "{}" { + t.Fatalf("unset enabled = %s, err = %v", data, err) + } +} diff --git a/go/internal/e2e/compaction_e2e_test.go b/go/internal/e2e/compaction_e2e_test.go index e994cdfc7e..9bf2084e52 100644 --- a/go/internal/e2e/compaction_e2e_test.go +++ b/go/internal/e2e/compaction_e2e_test.go @@ -2,6 +2,7 @@ package e2e import ( "errors" + "os" "strings" "testing" "time" @@ -118,8 +119,40 @@ func TestCompactionE2E(t *testing.T) { } if completeData.CompactionTokensUsed == nil { t.Errorf("Expected compaction tokens-used data") - } else if completeData.CompactionTokensUsed.InputTokens == nil || *completeData.CompactionTokensUsed.InputTokens <= 0 { - t.Errorf("Expected compaction call to consume input tokens, got %v", completeData.CompactionTokensUsed.InputTokens) + } else { + actualInputTokens := completeData.CompactionTokensUsed.InputTokens + // Replay has no prompt usage; recording must preserve the provider's value or absence. + if os.Getenv("GITHUB_ACTIONS") == "true" { + if actualInputTokens != nil && *actualInputTokens != 0 { + t.Errorf("Expected replay compaction input tokens to be zero or unavailable, got %d", *actualInputTokens) + } + } else { + var expectedInputTokens *int64 + exchanges, err := ctx.GetExchanges() + if err != nil { + t.Fatalf("Failed to get provider responses: %v", err) + } + var compactions []*testharness.CompactionProviderUsage + for _, exchange := range exchanges { + if exchange.CompactionUsage != nil && + completeData.SummaryContent != nil && exchange.CompactionUsage.Summary == *completeData.SummaryContent { + compactions = append(compactions, exchange.CompactionUsage) + } + } + if len(compactions) != 1 { + t.Fatalf("Expected one provider compaction chain for the completed summary, got %d", len(compactions)) + } + expectedInputTokens = compactions[0].InputTokens + if expectedInputTokens == nil { + if actualInputTokens != nil { + t.Errorf("Expected provider input tokens to be unavailable, got %d", *actualInputTokens) + } + } else if actualInputTokens == nil { + t.Errorf("Expected provider input tokens %d, got unavailable", *expectedInputTokens) + } else if *actualInputTokens != *expectedInputTokens { + t.Errorf("Expected provider input tokens %d, got %d", *expectedInputTokens, *actualInputTokens) + } + } } summary := "" if completeData.SummaryContent != nil { diff --git a/go/internal/e2e/hooks_extended_e2e_test.go b/go/internal/e2e/hooks_extended_e2e_test.go index 5cbba38566..2c804eba99 100644 --- a/go/internal/e2e/hooks_extended_e2e_test.go +++ b/go/internal/e2e/hooks_extended_e2e_test.go @@ -13,13 +13,13 @@ import ( // Mirrors dotnet/test/HookLifecycleAndOutputTests.cs (snapshot category "hooks_extended"). // -// Covers each handler exposed on copilot.SessionHooks: OnPreToolUse, +// Covers the general-purpose handlers on copilot.SessionHooks: OnPreToolUse, // OnPostToolUse, OnPostToolUseFailure, OnUserPromptSubmitted, // OnUserPromptTransformed, OnSessionStart, OnSessionEnd, OnErrorOccurred, -// OnAgentStop. Output-shape behavior (modifiedPrompt / modifiedTransformedPrompt / +// OnAgentStop. Subagent hooks are exercised in subagent_hooks_e2e_test.go. +// Output-shape behavior (modifiedPrompt / modifiedTransformedPrompt / // additionalContext / errorHandling / modifiedArgs / modifiedResult / -// sessionSummary) is asserted alongside hook invocation. If a new handler is -// added to SessionHooks, add a corresponding test here. +// sessionSummary) is asserted alongside hook invocation. func TestHooksExtendedE2E(t *testing.T) { ctx := testharness.NewTestContext(t) client := ctx.NewClient() diff --git a/go/internal/e2e/runtime_host_e2e_test.go b/go/internal/e2e/runtime_host_e2e_test.go index b30325ca72..8f1ae6c4b7 100644 --- a/go/internal/e2e/runtime_host_e2e_test.go +++ b/go/internal/e2e/runtime_host_e2e_test.go @@ -72,6 +72,8 @@ type ahpApplication struct { session atomic.Pointer[copilot.Session] creates atomic.Int32 resumes atomic.Int32 + createID atomic.Value + resumeID atomic.Value tools atomic.Int32 hooks atomic.Int32 released chan *copilot.Session @@ -122,13 +124,21 @@ func (a *ahpApplication) options() *copilot.AhpHostOptions { if ctx.Err() != nil || request.Config.WorkingDirectory != a.workDir { return nil, fmt.Errorf("invalid create handoff") } + if request.Config.SessionID == "" { + return nil, fmt.Errorf("create handoff omitted runtime session identity") + } + a.createID.Store(request.Config.SessionID) a.createConfig(request.Config) session, err := a.client.CreateSession(ctx, request.Config) + if err == nil && session.SessionID != request.Config.SessionID { + return nil, fmt.Errorf("create returned %q, requested %q", session.SessionID, request.Config.SessionID) + } a.session.Store(session) return session, err }, ResumeSession: func(ctx context.Context, request copilot.AhpSessionResumeRequest) (*copilot.Session, error) { a.resumes.Add(1) + a.resumeID.Store(request.SessionID) config := request.Config if ctx.Err() != nil || config.WorkingDirectory != a.workDir || config.ContinuePendingWork == nil || *config.ContinuePendingWork { @@ -138,6 +148,9 @@ func (a *ahpApplication) options() *copilot.AhpHostOptions { config.SystemMessage = &copilot.SystemMessageConfig{Mode: "append", Content: ahpPromptMarker} config.Tools, config.Hooks = a.callbacks() session, err := a.client.ResumeSessionWithOptions(ctx, request.SessionID, config) + if err == nil && session.SessionID != request.SessionID { + return nil, fmt.Errorf("resume returned %q, requested %q", session.SessionID, request.SessionID) + } a.session.Store(session) return session, err }, @@ -228,7 +241,8 @@ func TestRuntimeHostE2E(t *testing.T) { driveAhp(t, ahp, map[string]any{"op": "attach", "clientId": clientID, "sessionId": sessionID}) } else { sessionID = driveAhp(t, ahp, map[string]any{"op": "create", "clientId": clientID, "workDir": ctx.WorkDir}).SessionID - if app.creates.Load() != 1 || app.session.Load().SessionID != sessionID { + if app.creates.Load() != 1 || app.session.Load().SessionID != app.createID.Load() || + app.session.Load().SessionID == sessionID { t.Fatal("create callback did not materialize the requested identity") } } @@ -279,6 +293,10 @@ func TestRuntimeHostE2E(t *testing.T) { } clientID, closeFirst := connectAhpTest(t, ahp, host, "") sessionID := driveAhp(t, ahp, map[string]any{"op": "create", "clientId": clientID, "workDir": ctx.WorkDir, "clientTools": true}).SessionID + runtimeID := first.session.Load().SessionID + if first.creates.Load() != 1 || first.createID.Load() != runtimeID || runtimeID == sessionID { + t.Fatal("create callback did not preserve its distinct runtime session identity") + } answer := driveAhp(t, ahp, map[string]any{"op": "turn", "clientId": clientID, "sessionId": sessionID, "prompt": "What is 2+2?"}) if !strings.Contains(answer.Text, "4") { t.Fatal(answer.Text) @@ -310,7 +328,8 @@ func TestRuntimeHostE2E(t *testing.T) { t.Fatalf("lost durable history: %+v", history) } if resumed.creates.Load() != 0 || resumed.resumes.Load() != 1 || - resumed.session.Load() == first.session.Load() || resumed.session.Load().SessionID != sessionID { + resumed.resumeID.Load() != runtimeID || + resumed.session.Load() == first.session.Load() || resumed.session.Load().SessionID != runtimeID { t.Fatal("incorrect application resume callback or identity") } response := driveAhp(t, ahp, map[string]any{"op": "turn", "clientId": reconnected, "sessionId": sessionID, "prompt": ahpComposedPrompt, "clientTools": true}) diff --git a/go/internal/e2e/session_fs_e2e_test.go b/go/internal/e2e/session_fs_e2e_test.go index 4236e8e529..18d41a46b5 100644 --- a/go/internal/e2e/session_fs_e2e_test.go +++ b/go/internal/e2e/session_fs_e2e_test.go @@ -1,6 +1,7 @@ package e2e import ( + "encoding/base64" "fmt" "os" "path/filepath" @@ -76,6 +77,84 @@ func TestSessionFSE2E(t *testing.T) { } }) + t.Run("should view an image that exists only in the binary session fs provider", func(t *testing.T) { + ctx.ConfigureForTest(t) + + imagePath := "/sdk-provider-image.png" + imageBytes, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==") + if err != nil { + t.Fatal(err) + } + binaryConfig := *sessionFSConfig + binaryConfig.Capabilities = &copilot.SessionFSCapabilities{Binary: true} + binaryClient := ctx.NewClient(func(opts *copilot.ClientOptions) { + opts.SessionFS = &binaryConfig + }) + t.Cleanup(func() { binaryClient.ForceStop() }) + readPaths := make(chan string, 1) + session, err := binaryClient.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + CreateSessionFSProvider: func(session *copilot.Session) copilot.SessionFSProvider { + return &testBinarySessionFSHandler{ + testSessionFSHandler: &testSessionFSHandler{root: providerRoot, sessionID: session.SessionID}, + readPaths: readPaths, + } + }, + }) + if err != nil { + t.Fatalf("Failed to create binary session: %v", err) + } + storedPath := p(session.SessionID, imagePath) + if err := os.MkdirAll(filepath.Dir(storedPath), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(storedPath, imageBytes, 0o644); err != nil { + t.Fatal(err) + } + if _, err := os.Stat(imagePath); !os.IsNotExist(err) { + t.Fatalf("Image unexpectedly exists on the runtime filesystem: %v", err) + } + + msg, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ + Prompt: "Use the view tool to view /sdk-provider-image.png, then reply with exactly SDK_PROVIDER_IMAGE_DONE.", + }) + if err != nil { + t.Fatalf("Failed to view provider image: %v", err) + } + if msg == nil { + t.Fatal("Expected a final assistant response") + } + response, ok := msg.Data.(*copilot.AssistantMessageData) + if !ok || !strings.Contains(response.Content, "SDK_PROVIDER_IMAGE_DONE") { + t.Fatalf("Expected final image response, got %#v", msg) + } + select { + case path := <-readPaths: + if path != imagePath { + t.Fatalf("Expected binary read of %q, got %q", imagePath, path) + } + default: + t.Fatal("Expected a binary read from the provider") + } + events, err := session.GetEvents(t.Context()) + if err != nil { + t.Fatalf("Failed to read session events: %v", err) + } + foundAsset := false + for _, event := range events { + if asset, ok := event.Data.(*copilot.SessionBinaryAssetData); ok && + asset.MIMEType == "image/png" && asset.Data == base64.StdEncoding.EncodeToString(imageBytes) { + foundAsset = true + } + } + if !foundAsset { + t.Fatal("Expected a binary asset with the exact provider image bytes") + } + if err := session.Disconnect(); err != nil { + t.Fatalf("Failed to disconnect session: %v", err) + } + }) + t.Run("should load session data from fs provider on resume", func(t *testing.T) { ctx.ConfigureForTest(t) @@ -346,6 +425,31 @@ type testSessionFSHandler struct { sessionID string } +type testBinarySessionFSHandler struct { + *testSessionFSHandler + readPaths chan<- string +} + +func (h *testBinarySessionFSHandler) ReadFileBytes(path string) ([]byte, error) { + select { + case h.readPaths <- path: + default: + } + return os.ReadFile(providerPath(h.root, h.sessionID, path)) +} + +func (h *testBinarySessionFSHandler) WriteFileBytes(path string, content []byte, mode *int) error { + fullPath := providerPath(h.root, h.sessionID, path) + if err := os.MkdirAll(filepath.Dir(fullPath), 0o755); err != nil { + return err + } + perm := os.FileMode(0o666) + if mode != nil { + perm = os.FileMode(*mode) + } + return os.WriteFile(fullPath, content, perm) +} + func (h *testSessionFSHandler) ReadFile(path string) (string, error) { content, err := os.ReadFile(providerPath(h.root, h.sessionID, path)) if err != nil { diff --git a/go/internal/e2e/set_tools_e2e_test.go b/go/internal/e2e/set_tools_e2e_test.go new file mode 100644 index 0000000000..75b818fa9d --- /dev/null +++ b/go/internal/e2e/set_tools_e2e_test.go @@ -0,0 +1,311 @@ +package e2e + +import ( + "fmt" + "slices" + "strings" + "sync" + "testing" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/internal/e2e/testharness" +) + +const ( + fruitPrompt = "Use lookup_fruit to find the fruit for code 42." + fruitAndVegetablePrompt = "Use lookup_fruit to find the fruit for code 42 again, and use lookup_vegetable to find the vegetable for code 7." + vegetablePrompt = "Use lookup_vegetable to find the vegetable for code 7." +) + +func TestSetToolsE2E(t *testing.T) { + ctx := testharness.NewTestContext(t) + client := ctx.NewClient() + t.Cleanup(client.ForceStop) + + t.Run("replaces_tools_on_a_created_session", func(t *testing.T) { + ctx.ConfigureSnapshot(t, "set_tools/replaces_tools_on_a_created_session") + var originalLookups, replacementLookups, vegetableLookups toolCallRecorder + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Tools: []copilot.Tool{ + lookupFruitTool("apple", &originalLookups), + retiredLookupTool(), + }, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + first, err := session.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: fruitPrompt}) + if err != nil { + t.Fatalf("first SendAndWait failed: %v", err) + } + assertAssistantContains(t, first, "apple") + + if err := session.SetTools(t.Context(), []copilot.Tool{ + lookupFruitTool("dragonfruit", &replacementLookups), + lookupVegetableTool(&vegetableLookups), + }); err != nil { + t.Fatalf("SetTools failed: %v", err) + } + + second, err := session.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: fruitAndVegetablePrompt}) + if err != nil { + t.Fatalf("second SendAndWait failed: %v", err) + } + assertAssistantContains(t, second, "dragonfruit") + assertAssistantContains(t, second, "carrot") + assertCalls(t, "original fruit lookups", originalLookups.calls(), []int{42}) + assertCalls(t, "replacement fruit lookups", replacementLookups.calls(), []int{42}) + assertCalls(t, "vegetable lookups", vegetableLookups.calls(), []int{7}) + + // Model requests after the replacement offer exactly the new tool set. + exchanges, err := ctx.GetExchanges() + if err != nil { + t.Fatalf("GetExchanges failed: %v", err) + } + replacedFrom := indexOfPrompt(exchanges, fruitAndVegetablePrompt) + if replacedFrom <= 0 { + t.Fatalf("replacement prompt first sent in exchange %d, want an earlier exchange before it", replacedFrom) + } + assertOffered(t, exchanges[:replacedFrom], []string{"lookup_fruit", "retired_lookup"}, []string{"lookup_vegetable"}) + assertOffered(t, exchanges[replacedFrom:], []string{"lookup_fruit", "lookup_vegetable"}, []string{"retired_lookup"}) + }) + + t.Run("replaces_tools_on_a_resumed_session", func(t *testing.T) { + ctx.ConfigureSnapshot(t, "set_tools/replaces_tools_on_a_resumed_session") + var createdLookups toolCallRecorder + created, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Tools: []copilot.Tool{lookupFruitTool("apple", &createdLookups)}, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + sessionID := created.SessionID + first, err := created.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: fruitPrompt}) + if err != nil { + t.Fatalf("first SendAndWait failed: %v", err) + } + assertAssistantContains(t, first, "apple") + assertCalls(t, "created fruit lookups", createdLookups.calls(), []int{42}) + if err := created.Disconnect(); err != nil { + t.Fatalf("Disconnect failed: %v", err) + } + + var fruitLookups, vegetableLookups toolCallRecorder + resumed, err := client.ResumeSession(t.Context(), sessionID, &copilot.ResumeSessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Tools: []copilot.Tool{lookupFruitTool("apple", &fruitLookups)}, + }) + if err != nil { + t.Fatalf("ResumeSession failed: %v", err) + } + t.Cleanup(func() { _ = resumed.Disconnect() }) + if err := resumed.SetTools(t.Context(), []copilot.Tool{lookupVegetableTool(&vegetableLookups)}); err != nil { + t.Fatalf("SetTools failed: %v", err) + } + + answer, err := resumed.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: vegetablePrompt}) + if err != nil { + t.Fatalf("SendAndWait failed: %v", err) + } + assertAssistantContains(t, answer, "carrot") + assertCalls(t, "vegetable lookups", vegetableLookups.calls(), []int{7}) + assertCalls(t, "fruit lookups", fruitLookups.calls(), nil) + + exchanges, err := ctx.GetExchanges() + if err != nil { + t.Fatalf("GetExchanges failed: %v", err) + } + replacedFrom := indexOfPrompt(exchanges, vegetablePrompt) + if replacedFrom <= 0 { + t.Fatalf("replacement prompt first sent in exchange %d, want an earlier exchange before it", replacedFrom) + } + assertOffered(t, exchanges[replacedFrom:], []string{"lookup_vegetable"}, []string{"lookup_fruit"}) + }) + + t.Run("keeps_the_previous_tools_when_a_replacement_is_rejected", func(t *testing.T) { + ctx.ConfigureSnapshot(t, "set_tools/keeps_the_previous_tools_when_a_replacement_is_rejected") + var originalLookups, replacementLookups toolCallRecorder + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Tools: []copilot.Tool{lookupFruitTool("apple", &originalLookups)}, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + err = session.SetTools(t.Context(), []copilot.Tool{ + lookupFruitTool("dragonfruit", &replacementLookups), + invalidTool(), + }) + if err == nil { + t.Fatal("SetTools succeeded; want runtime rejection") + } + + answer, err := session.SendAndWait(t.Context(), copilot.MessageOptions{Prompt: fruitPrompt}) + if err != nil { + t.Fatalf("SendAndWait failed: %v", err) + } + assertAssistantContains(t, answer, "apple") + assertCalls(t, "original fruit lookups", originalLookups.calls(), []int{42}) + assertCalls(t, "replacement fruit lookups", replacementLookups.calls(), nil) + }) +} + +type toolCallRecorder struct { + mu sync.Mutex + codes []int +} + +func (r *toolCallRecorder) record(code int) { + if r == nil { + return + } + r.mu.Lock() + defer r.mu.Unlock() + r.codes = append(r.codes, code) +} + +func (r *toolCallRecorder) calls() []int { + r.mu.Lock() + defer r.mu.Unlock() + return append([]int(nil), r.codes...) +} + +func lookupFruitTool(fruit string, calls *toolCallRecorder) copilot.Tool { + return numericCodeTool("lookup_fruit", "Looks up the fruit for a numeric code", "Fruit code", func(code int) string { + calls.record(code) + return fruit + }) +} + +func lookupVegetableTool(calls *toolCallRecorder) copilot.Tool { + return numericCodeTool("lookup_vegetable", "Looks up the vegetable for a numeric code", "Vegetable code", func(code int) string { + calls.record(code) + return "carrot" + }) +} + +func retiredLookupTool() copilot.Tool { + return copilot.Tool{ + Name: "retired_lookup", + Description: "Looks up a retired value", + Handler: func(_ copilot.ToolInvocation) (copilot.ToolResult, error) { + return copilot.ToolResult{TextResultForLLM: "retired"}, nil + }, + } +} + +func invalidTool() copilot.Tool { + return copilot.Tool{ + Name: "invalid.tool", + Description: "Has a name the runtime rejects", + Handler: func(_ copilot.ToolInvocation) (copilot.ToolResult, error) { + return copilot.ToolResult{TextResultForLLM: "never"}, nil + }, + } +} + +func numericCodeTool(name, description, codeDescription string, handler func(int) string) copilot.Tool { + return copilot.Tool{ + Name: name, + Description: description, + Parameters: map[string]any{ + "type": "object", + "properties": map[string]any{ + "code": map[string]any{ + "type": "integer", + "description": codeDescription, + }, + }, + "required": []string{"code"}, + }, + Handler: func(inv copilot.ToolInvocation) (copilot.ToolResult, error) { + code, err := codeArgument(inv.Arguments) + if err != nil { + return copilot.ToolResult{}, err + } + return copilot.ToolResult{TextResultForLLM: handler(code)}, nil + }, + } +} + +func codeArgument(arguments any) (int, error) { + args, ok := arguments.(map[string]any) + if !ok { + return 0, fmt.Errorf("arguments = %#v, want object", arguments) + } + switch code := args["code"].(type) { + case float64: + return int(code), nil + case int: + return code, nil + default: + return 0, fmt.Errorf("code argument = %#v, want number", args["code"]) + } +} + +func assertAssistantContains(t *testing.T, event *copilot.SessionEvent, want string) { + t.Helper() + if event == nil { + t.Fatalf("assistant response is nil, want content containing %q", want) + } + data, ok := event.Data.(*copilot.AssistantMessageData) + if !ok { + t.Fatalf("event data = %T, want AssistantMessageData", event.Data) + } + if !strings.Contains(data.Content, want) { + t.Fatalf("assistant response = %q, want content containing %q", data.Content, want) + } +} + +func assertCalls(t *testing.T, label string, got, want []int) { + t.Helper() + if len(got) != len(want) { + t.Fatalf("%s = %v, want %v", label, got, want) + } + for i := range got { + if got[i] != want[i] { + t.Fatalf("%s = %v, want %v", label, got, want) + } + } +} + +// indexOfPrompt returns the index of the first model request that carries +// prompt as a user message, or -1. +func indexOfPrompt(exchanges []testharness.ParsedHttpExchange, prompt string) int { + for i, exchange := range exchanges { + for _, message := range exchange.Request.Messages { + if message.Role == "user" && (strings.Contains(message.Content, prompt) || strings.Contains(string(message.RawContent), prompt)) { + return i + } + } + } + return -1 +} + +// assertOffered checks that every exchange offered the tools in offered and +// none of the tools in notOffered. +func assertOffered(t *testing.T, exchanges []testharness.ParsedHttpExchange, offered, notOffered []string) { + t.Helper() + for _, exchange := range exchanges { + tools := make([]string, 0, len(exchange.Request.Tools)) + for _, tool := range exchange.Request.Tools { + tools = append(tools, tool.Function.Name) + } + for _, name := range offered { + if !slices.Contains(tools, name) { + t.Fatalf("model request offered %v, want it to include %q", tools, name) + } + } + for _, name := range notOffered { + if slices.Contains(tools, name) { + t.Fatalf("model request offered %v, want it to omit %q", tools, name) + } + } + } +} diff --git a/go/internal/e2e/skill_provider_e2e_test.go b/go/internal/e2e/skill_provider_e2e_test.go new file mode 100644 index 0000000000..d6c2dc93fb --- /dev/null +++ b/go/internal/e2e/skill_provider_e2e_test.go @@ -0,0 +1,586 @@ +package e2e + +import ( + "context" + "encoding/json" + "errors" + "os" + "path/filepath" + "strings" + "sync" + "testing" + "time" + + copilot "github.com/github/copilot-sdk/go" + "github.com/github/copilot-sdk/go/internal/e2e/testharness" + "github.com/github/copilot-sdk/go/rpc" +) + +type providedSkill struct { + descriptor rpc.SkillProviderDescriptor + read func() (string, error) +} + +type testSkillProvider struct { + mu sync.Mutex + skills []providedSkill + calls []string +} + +func newTestSkillProvider(skills []providedSkill) *testSkillProvider { + return &testSkillProvider{skills: skills} +} + +func providedSkillWithMarkdown(name, description, markdown string) providedSkill { + return providedSkill{ + descriptor: rpc.SkillProviderDescriptor{Name: name, Description: description}, + read: func() (string, error) { return markdown, nil }, + } +} + +func (p *testSkillProvider) ListSkills(ctx context.Context) ([]rpc.SkillProviderDescriptor, error) { + p.mu.Lock() + defer p.mu.Unlock() + p.calls = append(p.calls, "list") + skills := make([]rpc.SkillProviderDescriptor, 0, len(p.skills)) + for _, skill := range p.skills { + skills = append(skills, skill.descriptor) + } + return skills, nil +} + +func (p *testSkillProvider) ReadSkill(ctx context.Context, name string) (string, error) { + p.mu.Lock() + defer p.mu.Unlock() + p.calls = append(p.calls, "read:"+name) + for _, skill := range p.skills { + if skill.descriptor.Name == name { + return skill.read() + } + } + return "", copilot.ErrSkillNotFound +} + +func (p *testSkillProvider) Calls() []string { + p.mu.Lock() + defer p.mu.Unlock() + calls := make([]string, len(p.calls)) + copy(calls, p.calls) + return calls +} + +func (p *testSkillProvider) Reads() []string { + calls := p.Calls() + reads := make([]string, 0, len(calls)) + for _, call := range calls { + if strings.HasPrefix(call, "read:") { + reads = append(reads, strings.TrimPrefix(call, "read:")) + } + } + return reads +} + +// blockingSkillProvider blocks ListSkills until its context is cancelled. +type blockingSkillProvider struct { + entered chan struct{} + cancelled chan struct{} + enterOnce sync.Once + cancelOnce sync.Once +} + +func newBlockingSkillProvider() *blockingSkillProvider { + return &blockingSkillProvider{entered: make(chan struct{}), cancelled: make(chan struct{})} +} + +func (p *blockingSkillProvider) ListSkills(ctx context.Context) ([]rpc.SkillProviderDescriptor, error) { + p.enterOnce.Do(func() { close(p.entered) }) + <-ctx.Done() + p.cancelOnce.Do(func() { close(p.cancelled) }) + return nil, ctx.Err() +} + +func (p *blockingSkillProvider) ReadSkill(context.Context, string) (string, error) { + return "", copilot.ErrSkillNotFound +} + +func TestSkillProviderE2E(t *testing.T) { + ctx := testharness.NewTestContext(t) + client := ctx.NewClient() + t.Cleanup(func() { client.ForceStop() }) + + t.Run("should load provider skill lazily through skill tool", func(t *testing.T) { + ctx.ConfigureForTest(t) + provider := newTestSkillProvider([]providedSkill{ + providedSkillWithMarkdown( + "provider-lookup", + "Reports the provider lookup verification word.", + "# Provider lookup\n\nThe verification word is TANGERINE_QUARTZ_19. Reply with it.\n", + ), + }) + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + SkillProvider: provider, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + list, err := session.RPC.Skills.List(t.Context()) + if err != nil { + t.Fatalf("Skills.List failed: %v", err) + } + listed := findSkill(list, "provider-lookup") + if listed == nil { + t.Fatal("Expected provider-lookup skill to be listed") + } + if listed.Source != rpc.SkillSourceSDK || !listed.Enabled { + t.Fatalf("Expected provider-lookup source=sdk enabled=true, got %+v", listed) + } + if listed.Path != nil && *listed.Path != "" { + t.Fatalf("Expected provider-lookup path to be empty, got %q", *listed.Path) + } + if reads := provider.Reads(); len(reads) != 0 { + t.Fatalf("Provider reads before skill load = %v, want []", reads) + } + + message, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ + Prompt: "Use the skill tool to load the provider-lookup skill, then reply with its verification word.", + }) + if err != nil { + t.Fatalf("SendAndWait failed: %v", err) + } + + assertEqualStrings(t, provider.Reads(), []string{"provider-lookup"}) + // Validate the final assistant response arrived (guards against truncated captures) + assertAssistantContains(t, message, "TANGERINE_QUARTZ_19") + }) + + t.Run("should load provider and file based skills together", func(t *testing.T) { + ctx.ConfigureForTest(t) + skillsDir := filepath.Join(ctx.WorkDir, "file-skills") + fileSkillDir := filepath.Join(skillsDir, "file-notes") + if err := os.MkdirAll(fileSkillDir, 0755); err != nil { + t.Fatalf("MkdirAll failed: %v", err) + } + if err := os.WriteFile( + filepath.Join(fileSkillDir, "SKILL.md"), + []byte("---\nname: file-notes\ndescription: Reports the file notes verification word.\n---\n\nThe file notes verification word is MAPLE_FALCON_27.\n"), + 0644, + ); err != nil { + t.Fatalf("WriteFile failed: %v", err) + } + provider := newTestSkillProvider([]providedSkill{ + providedSkillWithMarkdown( + "provider-audit", + "Reports the provider audit verification word.", + "---\nname: provider-audit\nallowed-tools: view\n---\n\nThe provider audit verification word is COBALT_HERON_58.\n", + ), + }) + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + SkillDirectories: []string{skillsDir}, + SkillProvider: provider, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + list, err := session.RPC.Skills.List(t.Context()) + if err != nil { + t.Fatalf("Skills.List failed: %v", err) + } + fileSkill := findSkill(list, "file-notes") + providerSkill := findSkill(list, "provider-audit") + if fileSkill == nil || fileSkill.Source == rpc.SkillSourceSDK || fileSkill.Path == nil || *fileSkill.Path == "" { + t.Fatalf("Unexpected file-notes skill: %+v", fileSkill) + } + if providerSkill == nil || providerSkill.Source != rpc.SkillSourceSDK { + t.Fatalf("Unexpected provider-audit skill: %+v", providerSkill) + } + + message, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ + Prompt: "Use the skill tool to load the file-notes skill and the provider-audit skill, then reply with both verification words.", + }) + if err != nil { + t.Fatalf("SendAndWait failed: %v", err) + } + + assertEqualStrings(t, provider.Reads(), []string{"provider-audit"}) + assertAssistantContains(t, message, "MAPLE_FALCON_27") + // Validate the final assistant response arrived (guards against truncated captures) + assertAssistantContains(t, message, "COBALT_HERON_58") + }) + + t.Run("should rebind skill provider on resume", func(t *testing.T) { + ctx.ConfigureForTest(t) + original := newTestSkillProvider([]providedSkill{ + providedSkillWithMarkdown( + "rebind-check", + "Reports the rebind verification word.", + "The rebind verification word is AMBER_ALPHA_11.\n", + ), + }) + replacement := newTestSkillProvider([]providedSkill{ + providedSkillWithMarkdown( + "rebind-check", + "Reports the rebind verification word.", + "The rebind verification word is BRONZE_BETA_22.\n", + ), + }) + first, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + SkillProvider: original, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + sessionID := first.SessionID + if _, err := first.SendAndWait(t.Context(), copilot.MessageOptions{ + Prompt: "Without using any tools or skills, reply with exactly REBIND_READY.", + }); err != nil { + t.Fatalf("Initial SendAndWait failed: %v", err) + } + if err := first.Disconnect(); err != nil { + t.Fatalf("Disconnect failed: %v", err) + } + if reads := original.Reads(); len(reads) != 0 { + t.Fatalf("Original provider reads before resume = %v, want []", reads) + } + originalCallsBeforeResume := len(original.Calls()) + + session, err := client.ResumeSession(t.Context(), sessionID, &copilot.ResumeSessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + SkillProvider: replacement, + }) + if err != nil { + t.Fatalf("ResumeSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + message, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ + Prompt: "Use the skill tool to load the rebind-check skill, then reply with its verification word.", + }) + if err != nil { + t.Fatalf("SendAndWait failed: %v", err) + } + + assertEqualStrings(t, replacement.Reads(), []string{"rebind-check"}) + if got := len(original.Calls()); got != originalCallsBeforeResume { + t.Fatalf("Original provider call count after resume = %d, want %d", got, originalCallsBeforeResume) + } + // Validate the final assistant response arrived (guards against truncated captures) + assertAssistantContains(t, message, "BRONZE_BETA_22") + assertAssistantNotContains(t, message, "AMBER_ALPHA_11") + }) + + t.Run("should report provider read failure without leaking details", func(t *testing.T) { + ctx.ConfigureForTest(t) + secret := "PROVIDER_SECRET_7F3A9C" + provider := newTestSkillProvider([]providedSkill{ + { + descriptor: rpc.SkillProviderDescriptor{ + Name: "broken-lookup", + Description: "Reports the broken lookup verification word.", + }, + read: func() (string, error) { + return "", errors.New("database unavailable: " + secret) + }, + }, + }) + events := newEventRecorder() + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + SkillProvider: provider, + OnEvent: events.Record, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + message, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ + Prompt: "Use the skill tool to load the broken-lookup skill. If loading fails, reply with exactly LOAD_FAILED.", + }) + if err != nil { + t.Fatalf("SendAndWait failed: %v", err) + } + + assertContainsString(t, provider.Reads(), "broken-lookup") + failures := failedToolCompletions(events.Snapshot()) + if len(failures) != 1 { + t.Fatalf("Expected exactly 1 failed tool completion, got %d", len(failures)) + } + rawEvents, err := json.Marshal(events.Snapshot()) + if err != nil { + t.Fatalf("Marshal events failed: %v", err) + } + if strings.Contains(string(rawEvents), secret) { + t.Fatalf("Provider error leaked secret in events: %s", rawEvents) + } + // Validate the final assistant response arrived (guards against truncated captures) + assertAssistantContains(t, message, "LOAD_FAILED") + }) + + t.Run("should report missing provider skill as not found", func(t *testing.T) { + ctx.ConfigureForTest(t) + provider := newTestSkillProvider([]providedSkill{ + { + descriptor: rpc.SkillProviderDescriptor{ + Name: "vanished-lookup", + Description: "Reports the vanished lookup verification word.", + }, + read: func() (string, error) { + return "", copilot.ErrSkillNotFound + }, + }, + }) + events := newEventRecorder() + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + SkillProvider: provider, + OnEvent: events.Record, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + message, err := session.SendAndWait(t.Context(), copilot.MessageOptions{ + Prompt: "Use the skill tool to load the vanished-lookup skill. If loading fails, reply with exactly LOAD_FAILED.", + }) + if err != nil { + t.Fatalf("SendAndWait failed: %v", err) + } + + assertContainsString(t, provider.Reads(), "vanished-lookup") + failures := failedToolCompletions(events.Snapshot()) + if len(failures) != 1 { + t.Fatalf("Expected exactly 1 failed tool completion, got %d", len(failures)) + } + rawFailure, err := json.Marshal(failures[0]) + if err != nil { + t.Fatalf("Marshal failure failed: %v", err) + } + if !strings.Contains(strings.ToLower(string(rawFailure)), "not found") { + t.Fatalf("Expected failure to mention not found, got %s", rawFailure) + } + // Validate the final assistant response arrived (guards against truncated captures) + assertAssistantContains(t, message, "LOAD_FAILED") + }) + + t.Run("should keep provider dormant when skills disabled", func(t *testing.T) { + ctx.ConfigureWithoutSnapshot(t) + provider := newTestSkillProvider([]providedSkill{ + providedSkillWithMarkdown("dormant-lookup", "Never listed.", "Never read.\n"), + }) + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + EnableSkills: copilot.Bool(false), + SkillProvider: provider, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + if _, err := session.RPC.Skills.EnsureLoaded(t.Context()); err != nil { + t.Fatalf("Skills.EnsureLoaded failed: %v", err) + } + list, err := session.RPC.Skills.List(t.Context()) + if err != nil { + t.Fatalf("Skills.List failed: %v", err) + } + if sdkSkills := sdkSkills(list); len(sdkSkills) != 0 { + t.Fatalf("Expected no sdk skills when disabled, got %+v", sdkSkills) + } + if calls := provider.Calls(); len(calls) != 0 { + t.Fatalf("Provider calls when skills disabled = %v, want []", calls) + } + }) + + t.Run("should unbind provider when resumed without one", func(t *testing.T) { + ctx.ConfigureWithoutSnapshot(t) + provider := newTestSkillProvider([]providedSkill{ + providedSkillWithMarkdown("unbound-lookup", "Reports the unbound lookup word.", "Unbound.\n"), + }) + first, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + SkillProvider: provider, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + before, err := first.RPC.Skills.List(t.Context()) + if err != nil { + t.Fatalf("Skills.List before resume failed: %v", err) + } + if findSkill(before, "unbound-lookup") == nil { + t.Fatal("Expected unbound-lookup before resume") + } + callsBeforeResume := len(provider.Calls()) + + session, err := client.ResumeSession(t.Context(), first.SessionID, &copilot.ResumeSessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + }) + if err != nil { + t.Fatalf("ResumeSession failed: %v", err) + } + t.Cleanup(func() { _ = session.Disconnect() }) + + if _, err := session.RPC.Skills.Reload(t.Context()); err != nil { + t.Fatalf("Skills.Reload failed: %v", err) + } + list, err := session.RPC.Skills.List(t.Context()) + if err != nil { + t.Fatalf("Skills.List after resume failed: %v", err) + } + if sdkSkills := sdkSkills(list); len(sdkSkills) != 0 { + t.Fatalf("Expected no sdk skills after unbound resume, got %+v", sdkSkills) + } + if got := len(provider.Calls()); got != callsBeforeResume { + t.Fatalf("Provider call count after unbound resume = %d, want %d", got, callsBeforeResume) + } + }) + + t.Run("should cancel a blocked provider call when the session disconnects", func(t *testing.T) { + ctx.ConfigureWithoutSnapshot(t) + provider := newBlockingSkillProvider() + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + SkillProvider: provider, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + + // The list RPC fails once the binding is removed; only the provider's + // cancellation matters here. + go func() { _, _ = session.RPC.Skills.List(context.Background()) }() + select { + case <-provider.entered: + case <-time.After(30 * time.Second): + t.Fatal("provider ListSkills was not called") + } + + if err := session.Disconnect(); err != nil { + t.Fatalf("Disconnect failed: %v", err) + } + select { + case <-provider.cancelled: + case <-time.After(10 * time.Second): + t.Fatal("provider context was not cancelled after Disconnect") + } + }) + + t.Run("should reject skill provider for cloud sessions", func(t *testing.T) { + ctx.ConfigureWithoutSnapshot(t) + provider := newTestSkillProvider([]providedSkill{ + providedSkillWithMarkdown("cloud-lookup", "Never listed.", "Never read.\n"), + }) + + _, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Cloud: &copilot.CloudSessionOptions{}, + SkillProvider: provider, + }) + if err == nil || err.Error() != "Skill providers are not supported for cloud sessions." { + t.Fatalf("CreateSession error = %v, want cloud skill provider rejection", err) + } + if calls := provider.Calls(); len(calls) != 0 { + t.Fatalf("Provider calls after cloud rejection = %v, want []", calls) + } + }) +} + +type eventRecorder struct { + mu sync.Mutex + events []copilot.SessionEvent +} + +func newEventRecorder() *eventRecorder { + return &eventRecorder{} +} + +func (r *eventRecorder) Record(event copilot.SessionEvent) { + r.mu.Lock() + defer r.mu.Unlock() + r.events = append(r.events, event) +} + +func (r *eventRecorder) Snapshot() []copilot.SessionEvent { + r.mu.Lock() + defer r.mu.Unlock() + events := make([]copilot.SessionEvent, len(r.events)) + copy(events, r.events) + return events +} + +func findSkill(list *rpc.SkillList, name string) *rpc.Skill { + if list == nil { + return nil + } + for i := range list.Skills { + if list.Skills[i].Name == name { + return &list.Skills[i] + } + } + return nil +} + +func sdkSkills(list *rpc.SkillList) []rpc.Skill { + if list == nil { + return nil + } + var matches []rpc.Skill + for _, skill := range list.Skills { + if skill.Source == rpc.SkillSourceSDK { + matches = append(matches, skill) + } + } + return matches +} + +func failedToolCompletions(events []copilot.SessionEvent) []*copilot.ToolExecutionCompleteData { + var failures []*copilot.ToolExecutionCompleteData + for _, event := range events { + if data, ok := event.Data.(*copilot.ToolExecutionCompleteData); ok && !data.Success { + failures = append(failures, data) + } + } + return failures +} + +func assertAssistantNotContains(t *testing.T, event *copilot.SessionEvent, text string) { + t.Helper() + data, ok := event.Data.(*copilot.AssistantMessageData) + if !ok { + t.Fatalf("Expected AssistantMessageData, got %T", event.Data) + } + if strings.Contains(data.Content, text) { + t.Fatalf("Expected assistant response not to contain %q, got %q", text, data.Content) + } +} + +func assertEqualStrings(t *testing.T, got, want []string) { + t.Helper() + if len(got) != len(want) { + t.Fatalf("strings = %v, want %v", got, want) + } + for i := range got { + if got[i] != want[i] { + t.Fatalf("strings = %v, want %v", got, want) + } + } +} + +func assertContainsString(t *testing.T, got []string, want string) { + t.Helper() + for _, value := range got { + if value == want { + return + } + } + t.Fatalf("strings = %v, want to contain %q", got, want) +} diff --git a/go/internal/e2e/subagent_hooks_e2e_test.go b/go/internal/e2e/subagent_hooks_e2e_test.go index 2f2cedf904..f74cb6355d 100644 --- a/go/internal/e2e/subagent_hooks_e2e_test.go +++ b/go/internal/e2e/subagent_hooks_e2e_test.go @@ -1,37 +1,52 @@ package e2e import ( + "bytes" + "encoding/json" + "io" "net/http" "os" "path/filepath" + "strings" "sync" "testing" + "time" copilot "github.com/github/copilot-sdk/go" "github.com/github/copilot-sdk/go/internal/e2e/testharness" "github.com/google/uuid" ) +const childContext = "Subagent start hook verified: read the requested file." +const stopResponsePrefix = "Subagent stop hook verified: " + type subagentRequestRecord struct { agentID string parentAgentID string interactionType string + childPrompt string } type recordingForwardingTransport struct { - inner http.RoundTripper - mu sync.Mutex - records []subagentRequestRecord + inner http.RoundTripper + mu sync.Mutex + records []subagentRequestRecord + modifiedParentRequest chan struct{} } func newRecordingForwardingTransport() *recordingForwardingTransport { inner := http.DefaultTransport.(*http.Transport).Clone() inner.DisableCompression = true - return &recordingForwardingTransport{inner: inner} + return &recordingForwardingTransport{inner: inner, modifiedParentRequest: make(chan struct{}, 1)} } func (rt *recordingForwardingTransport) RoundTrip(req *http.Request) (*http.Response, error) { if isInferenceURL(req.URL.String()) { + body, err := io.ReadAll(req.Body) + if err != nil { + return nil, err + } + req.Body = io.NopCloser(bytes.NewReader(body)) rctx := copilot.RequestContextFrom(req) record := subagentRequestRecord{} if rctx != nil { @@ -39,6 +54,50 @@ func (rt *recordingForwardingTransport) RoundTrip(req *http.Request) (*http.Resp record.parentAgentID = rctx.ParentAgentID record.interactionType = rctx.InteractionType } + if record.parentAgentID != "" { + var payload struct { + Messages []struct { + Role string `json:"role"` + Content json.RawMessage `json:"content"` + } `json:"messages"` + } + if err := json.Unmarshal(body, &payload); err != nil { + return nil, err + } + for _, message := range payload.Messages { + if message.Role == "user" && len(message.Content) > 0 { + var prompts []string + switch message.Content[0] { + case '"': + var prompt string + if err := json.Unmarshal(message.Content, &prompt); err != nil { + return nil, err + } + prompts = append(prompts, prompt) + case '[': + var parts []struct { + Text string `json:"text"` + } + if err := json.Unmarshal(message.Content, &parts); err != nil { + return nil, err + } + for _, part := range parts { + prompts = append(prompts, part.Text) + } + } + for _, prompt := range prompts { + if strings.Contains(prompt, childContext+"\n\n") { + record.childPrompt = prompt + } + } + } + } + } else if bytes.Contains(body, []byte(stopResponsePrefix)) { + select { + case rt.modifiedParentRequest <- struct{}{}: + default: + } + } rt.mu.Lock() rt.records = append(rt.records, record) rt.mu.Unlock() @@ -87,7 +146,7 @@ func TestSubagentHooksE2E(t *testing.T) { }) t.Cleanup(func() { client.ForceStop() }) - t.Run("should invoke preToolUse and postToolUse hooks for sub-agent tool calls", func(t *testing.T) { + t.Run("should apply subagent lifecycle hook outputs", func(t *testing.T) { ctx.ConfigureForTest(t) type hookEntry struct { @@ -103,6 +162,11 @@ func TestSubagentHooksE2E(t *testing.T) { parentWaiting := make(chan struct{}) releaseView := sync.OnceFunc(func() { close(parentWaiting) }) defer releaseView() + var startInputs []copilot.SubagentStartHookInput + var stopInputs []copilot.SubagentStopHookInput + var startInvocations []copilot.HookInvocation + var stopInvocations []copilot.HookInvocation + stopObserved := make(chan struct{}, 1) session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ SessionID: parentSessionID, @@ -128,12 +192,29 @@ func TestSubagentHooksE2E(t *testing.T) { } return nil, nil }, + OnSubagentStart: func(input copilot.SubagentStartHookInput, invocation copilot.HookInvocation) (*copilot.SubagentStartHookOutput, error) { + mu.Lock() + startInputs = append(startInputs, input) + startInvocations = append(startInvocations, invocation) + mu.Unlock() + return &copilot.SubagentStartHookOutput{AdditionalContext: childContext}, nil + }, + OnSubagentStop: func(input copilot.SubagentStopHookInput, invocation copilot.HookInvocation) (*copilot.SubagentStopHookOutput, error) { + mu.Lock() + stopInputs = append(stopInputs, input) + stopInvocations = append(stopInvocations, invocation) + mu.Unlock() + select { + case stopObserved <- struct{}{}: + default: + } + return &copilot.SubagentStopHookOutput{ModifiedResponse: copilot.String(stopResponsePrefix + input.Response)}, nil + }, }, }) if err != nil { t.Fatalf("Failed to create session: %v", err) } - // Create a file for the sub-agent to read testFile := filepath.Join(ctx.WorkDir, "subagent-test.txt") if err := os.WriteFile(testFile, []byte("Hello from subagent test!"), 0644); err != nil { @@ -173,8 +254,57 @@ func TestSubagentHooksE2E(t *testing.T) { t.Fatalf("Expected durable waiting reply followed by final reply, got %q", replies) } + select { + case <-stopObserved: + case <-time.After(120 * time.Second): + t.Fatal("Timed out waiting for the subagentStop hook") + } + select { + case <-transport.modifiedParentRequest: + case <-time.After(120 * time.Second): + t.Fatal("Timed out waiting for the parent inference request with the rewritten response") + } mu.Lock() defer mu.Unlock() + if len(startInputs) != 1 || len(stopInputs) != 1 { + t.Fatalf("Expected one subagentStart and one subagentStop invocation, got starts=%+v stops=%+v", startInputs, stopInputs) + } + start, stop := startInputs[0], stopInputs[0] + if startInvocations[0].SessionID != session.SessionID || stopInvocations[0].SessionID != session.SessionID || + start.SessionID != session.SessionID || stop.SessionID != session.SessionID { + t.Errorf("Expected parent session ID %q in hook inputs and invocations, got start=%+v stop=%+v", session.SessionID, start, stop) + } + if !start.Timestamp.After(time.UnixMilli(0)) || !stop.Timestamp.After(time.UnixMilli(0)) || + stop.Timestamp.Before(start.Timestamp) { + t.Errorf("Expected ordered timestamps, got start=%v stop=%v", start.Timestamp, stop.Timestamp) + } + if start.WorkingDirectory != ctx.WorkDir || stop.WorkingDirectory != ctx.WorkDir { + t.Errorf("Expected working directory %q, got start=%q stop=%q", ctx.WorkDir, start.WorkingDirectory, stop.WorkingDirectory) + } + if stop.TranscriptPath != start.TranscriptPath || (start.TranscriptPath != "" && !filepath.IsAbs(start.TranscriptPath)) { + t.Errorf("Expected matching transcript paths (absolute when available), got start=%q stop=%q", start.TranscriptPath, stop.TranscriptPath) + } + if start.AgentName != "explore" || stop.AgentName != start.AgentName || stop.AgentType != "explore" { + t.Errorf("Expected explore agent name and type, got start=%+v stop=%+v", start, stop) + } + if start.AgentDisplayName != "" { + t.Errorf("Expected no optional display name on this task-created agent, got %q", start.AgentDisplayName) + } + if stop.AgentDisplayName != start.AgentDisplayName { + t.Errorf("Expected stop display name %q, got %q", start.AgentDisplayName, stop.AgentDisplayName) + } + if start.AgentDescription != "" { + t.Errorf("Expected no optional description on this task-created agent, got %q", start.AgentDescription) + } + if stop.AgentDescription != start.AgentDescription { + t.Errorf("Expected stop description %q, got %q", start.AgentDescription, stop.AgentDescription) + } + if stop.AgentID == "" { + t.Error("Expected a runtime-generated sub-agent ID") + } + if stop.StopReason != "end_turn" || !strings.Contains(stop.Response, "Hello from subagent test!") { + t.Errorf("Expected completed turn and file contents in stop input, got %+v", stop) + } // Parent tool hooks fire for "task" var taskPre *hookEntry @@ -211,6 +341,16 @@ func TestSubagentHooksE2E(t *testing.T) { if viewPre[0].sessionID == taskPre.sessionID { t.Error("Sub-agent tool hooks should have a different sessionId than parent tool hooks") } - assertSubagentRequestMetadata(t, transport.inferenceRecords()) + requests := transport.inferenceRecords() + assertSubagentRequestMetadata(t, requests) + startContextObserved := false + for _, request := range requests { + if strings.Contains(request.childPrompt, childContext+"\n\nRead the file \"subagent-test.txt\"") { + startContextObserved = true + } + } + if !startContextObserved { + t.Errorf("Start hook context did not reach a child inference prompt: %+v", requests) + } }) } diff --git a/go/internal/e2e/testharness/proxy.go b/go/internal/e2e/testharness/proxy.go index e6a595ffe2..488215f8a6 100644 --- a/go/internal/e2e/testharness/proxy.go +++ b/go/internal/e2e/testharness/proxy.go @@ -154,10 +154,13 @@ func (p *CapiProxy) StopWithOptions(skipWritingCache bool) error { exited <- struct{}{} }() if !waitForProcessExit(exited, proxyShutdownTimeout) { - if err := killProcessTree(cmd); err != nil { - return fmt.Errorf("failed to kill proxy process: %w", err) - } + // Windows Wait can release the process handle before the kill attempt. + // A late kill error is harmless only when our own Wait confirms exit. + killErr := killProcessTree(cmd) if !waitForProcessExit(exited, proxyShutdownTimeout) { + if killErr != nil { + return fmt.Errorf("failed to kill proxy process: %w", killErr) + } return fmt.Errorf("proxy process did not exit after being killed") } } @@ -278,9 +281,18 @@ type CapturedRequest struct { // ParsedHttpExchange represents a captured HTTP exchange. type ParsedHttpExchange struct { - Request ChatCompletionRequest `json:"request"` - Response *ChatCompletionResponse `json:"response,omitempty"` - RequestHeaders map[string]json.RawMessage `json:"requestHeaders,omitempty"` + Request ChatCompletionRequest `json:"request"` + Response *ChatCompletionResponse `json:"response,omitempty"` + RequestHeaders map[string]json.RawMessage `json:"requestHeaders,omitempty"` + CompactionUsage *CompactionProviderUsage `json:"compactionUsage,omitempty"` +} + +// CompactionProviderUsage folds all responses in one correlated compaction request chain. +type CompactionProviderUsage struct { + InteractionID string `json:"interactionId"` + Summary string `json:"summary"` + ResponseCount int `json:"responseCount"` + InputTokens *int64 `json:"inputTokens,omitempty"` } // ChatCompletionRequest represents an OpenAI chat completion request. diff --git a/go/internal/e2e/testharness/proxy_test.go b/go/internal/e2e/testharness/proxy_test.go new file mode 100644 index 0000000000..cbd79c5548 --- /dev/null +++ b/go/internal/e2e/testharness/proxy_test.go @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package testharness + +import ( + "encoding/json" + "testing" +) + +func TestCompactionProviderUsageInputCounts(t *testing.T) { + for _, tc := range []struct { + name string + inputProperty string + want int64 + known bool + }{ + {"recorded", `,"inputTokens":37`, 37, true}, + {"replayed", `,"inputTokens":0`, 0, true}, + {"unavailable", ``, 0, false}, + {"input null", `,"inputTokens":null`, 0, false}, + } { + t.Run(tc.name, func(t *testing.T) { + var exchange ParsedHttpExchange + body := `{"compactionUsage":{"interactionId":"compaction-interaction","summary":"summary","responseCount":2` + + tc.inputProperty + `},"requestHeaders":{"x-interaction-type":"conversation-compaction"}}` + if err := json.Unmarshal([]byte(body), &exchange); err != nil { + t.Fatal(err) + } + if exchange.CompactionUsage == nil { + t.Fatal("Expected compaction provider usage") + } + var interactionType string + if err := json.Unmarshal(exchange.RequestHeaders["x-interaction-type"], &interactionType); err != nil || interactionType != "conversation-compaction" { + t.Fatalf("Compaction interaction header = %q, %v", interactionType, err) + } + usage := exchange.CompactionUsage + if usage.InteractionID != "compaction-interaction" || usage.Summary != "summary" || usage.ResponseCount != 2 { + t.Fatalf("Unexpected compaction chain metadata: %+v", usage) + } + var got int64 + known := usage.InputTokens != nil + if known != tc.known { + t.Fatalf("Provider input usage availability = %v, want %v", known, tc.known) + } + if known { + got = *usage.InputTokens + } + if got != tc.want { + t.Fatalf("Provider input tokens = %d, want %d", got, tc.want) + } + }) + } +} diff --git a/go/internal/e2e/tools_e2e_test.go b/go/internal/e2e/tools_e2e_test.go index 75613bebdf..a5109b154b 100644 --- a/go/internal/e2e/tools_e2e_test.go +++ b/go/internal/e2e/tools_e2e_test.go @@ -1,3 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + package e2e import ( @@ -18,6 +20,53 @@ func TestToolsE2E(t *testing.T) { client := ctx.NewClient() t.Cleanup(func() { client.ForceStop() }) + t.Run("string schema apply_patch override binds patch input", func(t *testing.T) { + ctx.ConfigureForTest(t) + const patch = "*** Begin Patch\n*** Add File: override-marker.txt\n+from-native\n*** End Patch" + type capturedPatch struct { + patch string + arguments any + } + calls := make(chan capturedPatch, 1) + tool := copilot.DefineTool("apply_patch", "Apply a patch", + func(input string, invocation copilot.ToolInvocation) (string, error) { + calls <- capturedPatch{input, invocation.Arguments} + return "HOST_PATCH_HANDLED", nil + }) + tool.OverridesBuiltInTool = true + session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{ + OnPermissionRequest: copilot.PermissionHandler.ApproveAll, + Tools: []copilot.Tool{tool}, + }) + if err != nil { + t.Fatalf("Failed to create session: %v", err) + } + finalMessage := testharness.SubscribeToFinalAssistantMessage(session) + defer finalMessage.Close() + _, err = session.Send(t.Context(), copilot.MessageOptions{Prompt: "Use apply_patch to apply the supplied patch."}) + if err != nil { + t.Fatalf("Failed to send message: %v", err) + } + answer, err := finalMessage.Wait(t.Context()) + if err != nil { + t.Fatalf("Failed to get assistant message: %v", err) + } + select { + case call := <-calls: + if call.patch != patch || call.arguments != patch { + t.Fatalf("Expected scalar patch %q, got %+v", patch, call) + } + default: + t.Fatal("String-schema handler was not invoked") + } + if message, ok := answer.Data.(*copilot.AssistantMessageData); !ok || message.Content != "Host override completed." { + t.Fatalf("Unexpected final response: %v", answer.Data) + } + if _, err := os.Stat(filepath.Join(ctx.WorkDir, "override-marker.txt")); !os.IsNotExist(err) { + t.Fatalf("Native apply_patch must not create the file: %v", err) + } + }) + t.Run("invokes built-in tools", func(t *testing.T) { ctx.ConfigureForTest(t) diff --git a/go/rpc/mcp_list_test.go b/go/rpc/mcp_list_test.go new file mode 100644 index 0000000000..7a2f3e0427 --- /dev/null +++ b/go/rpc/mcp_list_test.go @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package rpc + +import ( + "context" + "encoding/json" + "io" + "testing" + + "github.com/github/copilot-sdk/go/internal/jsonrpc2" +) + +var _ interface { + List(context.Context) (*MCPServerList, error) + ListConfigured(context.Context) (*MCPConfiguredServerList, error) +} = (*MCPAPI)(nil) + +func TestMCPListUsesParameterlessWireContract(t *testing.T) { + clientToServerReader, clientToServerWriter := io.Pipe() + serverToClientReader, serverToClientWriter := io.Pipe() + client := jsonrpc2.NewClient(clientToServerWriter, serverToClientReader) + server := jsonrpc2.NewClient(serverToClientWriter, clientToServerReader) + requests := make(chan map[string]any, 2) + server.SetRequestHandler("session.mcp.list", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + var request map[string]any + if err := json.Unmarshal(params, &request); err != nil { + return nil, &jsonrpc2.Error{Code: -32602, Message: err.Error()} + } + requests <- request + return json.RawMessage(`{"servers":[]}`), nil + }) + server.SetRequestHandler("session.mcp.listConfigured", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + var request map[string]any + if err := json.Unmarshal(params, &request); err != nil { + return nil, &jsonrpc2.Error{Code: -32602, Message: err.Error()} + } + requests <- request + return json.RawMessage(`{"servers":[ + {"name":"cold","enabled":true,"live":{"status":"not_configured"}}, + {"name":"old-connection","enabled":false,"live":{"status":"connected"}}, + {"name":"auth","enabled":true,"live":{"status":"needs-auth"}}, + {"name":"stopped","enabled":true,"live":{"status":"stopped"}}, + {"name":"failed","enabled":true,"live":{"status":"failed","error":"connection failed"}} + ]}`), nil + }) + client.Start() + server.Start() + t.Cleanup(func() { + client.Stop() + server.Stop() + _ = clientToServerWriter.Close() + _ = clientToServerReader.Close() + _ = serverToClientWriter.Close() + _ = serverToClientReader.Close() + }) + + mcp := NewSessionRPC(client, "session-1").MCP + if _, err := mcp.List(t.Context()); err != nil { + t.Fatal(err) + } + configured, err := mcp.ListConfigured(t.Context()) + if err != nil { + t.Fatal(err) + } + want := []MCPServerStatus{"not_configured", "connected", "needs-auth", "stopped", "failed"} + if len(configured.Servers) != len(want) { + t.Fatalf("configured servers = %#v, want %d entries", configured.Servers, len(want)) + } + for i, status := range want { + live := configured.Servers[i].Live + if live.Status != status { + t.Fatalf("live status = %q, want %q", live.Status, status) + } + } + if configured.Servers[1].Enabled || configured.Servers[4].Live.Error == nil || + *configured.Servers[4].Live.Error != "connection failed" { + t.Fatalf("configuration enablement and runtime error were not preserved: %#v", configured.Servers) + } + got := make([]map[string]any, 2) + for i := range got { + got[i] = <-requests + } + for _, request := range got { + if request["sessionId"] != "session-1" || len(request) != 1 { + t.Fatalf("request = %#v, want only the bound session ID", request) + } + } +} diff --git a/go/rpc/mcp_prompts_test.go b/go/rpc/mcp_prompts_test.go new file mode 100644 index 0000000000..14cbe69fc9 --- /dev/null +++ b/go/rpc/mcp_prompts_test.go @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package rpc + +import ( + "encoding/json" + "os" + "path/filepath" + "reflect" + "testing" +) + +// Generated serialization supplements the real MCP boundary tests in Node and .NET. +func TestMCPPromptResultsPreserveOpaqueJSON(t *testing.T) { + raw, err := os.ReadFile(filepath.Join("..", "..", "test", "harness", "mcp-prompt-fixtures.json")) + if err != nil { + t.Fatal(err) + } + var fixtures map[string]json.RawMessage + if err := json.Unmarshal(raw, &fixtures); err != nil { + t.Fatal(err) + } + for _, name := range []string{"firstPage", "secondPage", "richPrompt"} { + t.Run(name, func(t *testing.T) { + var result any = &MCPPromptsListResult{} + if name == "richPrompt" { + result = &MCPPromptsGetResult{} + } + if err := json.Unmarshal(fixtures[name], result); err != nil { + t.Fatal(err) + } + assertPromptJSON(t, result, fixtures[name]) + }) + } +} + +func TestMCPPromptRequestsPreserveOptionalArguments(t *testing.T) { + for _, wire := range []string{ + `{"serverName":"fixture","promptName":"rich"}`, + `{"serverName":"fixture","promptName":"rich","arguments":{}}`, + `{"serverName":"fixture","promptName":"rich","arguments":{"topic":"日本語","style":""}}`, + } { + t.Run(wire, func(t *testing.T) { + var request MCPPromptsGetRequest + if err := json.Unmarshal([]byte(wire), &request); err != nil { + t.Fatal(err) + } + assertPromptJSON(t, request, []byte(wire)) + }) + } + for _, wire := range []string{ + `{"serverName":"fixture"}`, + `{"serverName":"fixture","cursor":"page:2/opaque+cursor="}`, + } { + var request MCPPromptsListRequest + if err := json.Unmarshal([]byte(wire), &request); err != nil { + t.Fatal(err) + } + assertPromptJSON(t, request, []byte(wire)) + } +} + +func assertPromptJSON(t *testing.T, value any, expected []byte) { + t.Helper() + actual, err := json.Marshal(value) + if err != nil { + t.Fatal(err) + } + var got, want any + if err := json.Unmarshal(actual, &got); err != nil { + t.Fatal(err) + } + if err := json.Unmarshal(expected, &want); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(got, want) { + t.Fatalf("JSON mismatch\n got: %s\nwant: %s", actual, expected) + } +} diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index 6a0f8c826f..7f341d8b5f 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -468,6 +468,37 @@ type AgentReloadResult struct { Agents []AgentInfo `json:"agents"` } +// The models a custom agent asks for, and the models actually available. +// Experimental: AgentsCustomAgentInitialModelDecisionParams is part of an experimental API +// and may change or be removed. +// Internal: AgentsCustomAgentInitialModelDecisionParams is an internal SDK API and is not +// part of the public surface. +type AgentsCustomAgentInitialModelDecisionParams struct { + // The agent's declared `model:` entry, serialized. A single name or an ordered list of + // acceptable names. + AgentModelsJSON string `json:"agentModelsJson"` + // The models available to this session, serialized in the shape the model list carries. + AvailableModelsJSON string `json:"availableModelsJson"` +} + +// The model to switch to, and the warning to show when the agent's preference could not be +// met. +// Experimental: AgentsCustomAgentInitialModelDecisionResult is part of an experimental API +// and may change or be removed. +// Internal: AgentsCustomAgentInitialModelDecisionResult is an internal SDK API and is not +// part of the public surface. +type AgentsCustomAgentInitialModelDecisionResult struct { + // The reasoning effort attached to the selected model preference. Absent when that + // preference does not specify an effort. + ReasoningEffort *string `json:"reasoningEffort,omitempty"` + // The first available model that matches the agent's preferences. Absent when none of the + // requested models is available. + TargetModel *string `json:"targetModel,omitempty"` + // 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. + Warning *string `json:"warning,omitempty"` +} + // Optional project paths to include in agent discovery. // Experimental: AgentsDiscoverRequest is part of an experimental API and may change or be // removed. @@ -506,6 +537,101 @@ type AgentSetPromptRequest struct { Prompt string `json:"prompt"` } +// The feature flags to evaluate shipped agents against. +// Experimental: AgentsGetAvailableBuiltinsRequest is part of an experimental API and may +// change or be removed. +// Internal: AgentsGetAvailableBuiltinsRequest is an internal SDK API and is not part of the +// public surface. +type AgentsGetAvailableBuiltinsRequest struct { + // The surface asking, which gates agents that only apply to one client. Omit or pass null + // to apply no client filter. + Context *string `json:"context,omitempty"` + // Feature flag values keyed by name, evaluated with the runtime's truthiness rules. Omit or + // pass null for no flags. + FeatureFlags map[string]any `json:"featureFlags,omitzero"` + // Flag overrides keyed by name. A null entry uses the corresponding base flag; false + // explicitly disables it. Omit or pass null for no overrides. + Overrides map[string]any `json:"overrides,omitzero"` +} + +// The shipped agents available under the requested flags. +// Experimental: AgentsGetAvailableBuiltinsResult is part of an experimental API and may +// change or be removed. +// Internal: AgentsGetAvailableBuiltinsResult is an internal SDK API and is not part of the +// public surface. +type AgentsGetAvailableBuiltinsResult struct { + // Available shipped agents, in the runtime's own order. + // Internal: Agents is part of the SDK's internal API surface and is not intended for + // external use. + Agents []BuiltinAgentSummary `json:"agents"` +} + +// The shipped agent whose definition to load. +// Experimental: AgentsGetBuiltinDefinitionRequest is part of an experimental API and may +// change or be removed. +// Internal: AgentsGetBuiltinDefinitionRequest is an internal SDK API and is not part of the +// public surface. +type AgentsGetBuiltinDefinitionRequest struct { + // 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. + Name string `json:"name"` +} + +// One shipped agent's definition. +// Experimental: AgentsGetBuiltinDefinitionResult is part of an experimental API and may +// change or be removed. +// Internal: AgentsGetBuiltinDefinitionResult is an internal SDK API and is not part of the +// public surface. +type AgentsGetBuiltinDefinitionResult struct { + // 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. + DefinitionJSON string `json:"definitionJson"` +} + +// The shipped agent whose listing entry to load. +// Experimental: AgentsGetBuiltinListingDefinitionRequest is part of an experimental API and +// may change or be removed. +// Internal: AgentsGetBuiltinListingDefinitionRequest is an internal SDK API and is not part +// of the public surface. +type AgentsGetBuiltinListingDefinitionRequest struct { + // 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. + Name string `json:"name"` +} + +// One shipped agent, projected for a listing. +// Experimental: AgentsGetBuiltinListingDefinitionResult is part of an experimental API and +// may change or be removed. +// Internal: AgentsGetBuiltinListingDefinitionResult is an internal SDK API and is not part +// of the public surface. +type AgentsGetBuiltinListingDefinitionResult struct { + // 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. + DefinitionJSON string `json:"definitionJson"` +} + +// The agents this runtime ships, named so a consumer can tell them apart from authored ones. +// Experimental: AgentsGetBuiltinsResult is part of an experimental API and may change or be +// removed. +// Internal: AgentsGetBuiltinsResult is an internal SDK API and is not part of the public +// surface. +type AgentsGetBuiltinsResult struct { + // 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. + DisableableNames []string `json:"disableableNames"` + // Every agent name this runtime ships. + Names []string `json:"names"` + // The subset of `names` defined by a shipped YAML definition. The remainder are + // special-cased in code and have no definition to load. + YamlBasedNames []string `json:"yamlBasedNames"` +} + // Optional project paths to include when enumerating agent discovery directories. // Experimental: AgentsGetDiscoveryPathsRequest is part of an experimental API and may // change or be removed. @@ -1034,6 +1160,21 @@ func (r RawAuthInfoData) Type() AuthInfoType { return r.Discriminator } +// An interactive account whose model provider owns its credentials. It carries no GitHub +// credential. +// Experimental: AccountAuthInfo is part of an experimental API and may change or be removed. +type AccountAuthInfo struct { + // Host coordinate owned by the account's model provider. + Host string `json:"host"` + // Login identifying the provider-owned account. + Login string `json:"login"` +} + +func (AccountAuthInfo) authInfo() {} +func (AccountAuthInfo) Type() AuthInfoType { + return AuthInfoTypeAccount +} + // Authentication-info input variant for API-key authentication to a non-GitHub LLM // provider, carrying the secret `apiKey` and host. // Experimental: APIKeyAuthInfo is part of an experimental API and may change or be removed. @@ -1193,6 +1334,20 @@ func (UserAuthInfo) Type() AuthInfoType { return AuthInfoTypeUser } +// A credential-free account choice after sign-in. +// Experimental: AuthLoginAccount is part of an experimental API and may change or be +// removed. +type AuthLoginAccount struct { + // Host coordinate owned by the selected account's provider. + Host string `json:"host"` + // Provider kind that owns this account choice. + Kind AccountKind `json:"kind"` + // Human-readable login for the account choice. + Login string `json:"login"` + // Opaque identifier supplied to the next login step to select this account. + SelectionID string `json:"selectionId"` +} + // Advance an in-flight login flow, optionally fulfilling an input-required step. // Experimental: AuthLoginAdvanceRequest is part of an experimental API and may change or be // removed. @@ -1229,15 +1384,18 @@ type AuthLoginCancelRequest struct { FlowID string `json:"flowId"` } -// Terminal result of an interactive login flow. +// Result of an interactive login flow. Pending consent or account selection is not terminal. // Experimental: AuthLoginResultDto is part of an experimental API and may change or be // removed. type AuthLoginResultDto struct { + // Available accounts when sign-in is awaiting account selection, ordered with Microsoft 365 + // first. + Accounts []AuthLoginAccount `json:"accounts,omitzero"` // Host that was signed in, when completed. Host *string `json:"host,omitempty"` // Login that was signed in, when completed. Login *string `json:"login,omitempty"` - // Terminal disposition of the login. + // Current disposition of the login, including pending user decisions. Status AuthLoginResultStatus `json:"status"` } @@ -1271,7 +1429,8 @@ func (AuthLoginStepAwaiting) Kind() AuthLoginStepKind { } type AuthLoginStepCompleted struct { - // The terminal login result. + // Login result. When status is needs-plaintext-consent or needs-account-selection, advance + // with the user's decision to continue. Result AuthLoginResultDto `json:"result"` } @@ -1380,6 +1539,9 @@ func (r RawAuthReadValueData) Kind() AuthReadValueKind { type AuthReadValueActiveAccount struct { // The active account, or absent when not logged in. Account *AccountStatus `json:"account,omitempty"` + // Credential-free identity metadata for the active account, including resolved Copilot user + // information when available. + AuthInfo *AuthIdentity `json:"authInfo,omitempty"` } func (AuthReadValueActiveAccount) authReadValue() {} @@ -1540,6 +1702,57 @@ type AutopilotObjectiveState struct { TurnCount int64 `json:"turnCount"` } +// A server-advertised routing preference. Identifiers and execution types are extensible. +// Experimental: AutoTierDescriptor is part of an experimental API and may change or be +// removed. +type AutoTierDescriptor struct { + // Description displayed beside the preference. + Description string `json:"description"` + // Human-readable label, not a routing identifier. + DisplayName string `json:"displayName"` + // Opaque routing identifier transmitted unchanged to the provider. + ID string `json:"id"` + // Current account-specific availability. + Status AutoTierStatus `json:"status"` + // Execution kind; this client supports `auto` preferences on the Auto model. + Type string `json:"type"` +} + +// Account-bound discovery metadata for the virtual `auto` model. +// Experimental: AutoTierMetadata is part of an experimental API and may change or be +// removed. +type AutoTierMetadata struct { + // Provider-default preference, used only when no explicit preference exists. + DefaultTier string `json:"defaultTier"` + // Provider that supplied this metadata, when the catalog is provider-attributed. + ProviderID *string `json:"providerId,omitempty"` + // Routing preferences in the server's presentation order. + Tiers []AutoTierDescriptor `json:"tiers"` +} + +// Availability of a server-advertised routing preference. +// Experimental: AutoTierStatus is part of an experimental API and may change or be removed. +type AutoTierStatus struct { + // Whether the provider permits selecting this preference. + Enabled bool `json:"enabled"` + // Human-readable explanation of availability. + Message *string `json:"message,omitempty"` + // Extensible machine-readable unavailability reason. + Reason *string `json:"reason,omitempty"` +} + +// A shipped agent, named and described. +// Experimental: BuiltinAgentSummary is part of an experimental API and may change or be +// removed. +// Internal: BuiltinAgentSummary is an internal SDK API and is not part of the public +// surface. +type BuiltinAgentSummary struct { + // One-line description of what the agent does. + Description string `json:"description"` + // The agent name, as it appears in `getBuiltins`. + Name string `json:"name"` +} + // The running runtime's complete catalog of well-known built-in model IDs, including // supported models and additional IDs with built-in metadata. // Experimental: BuiltInModelCatalog is part of an experimental API and may change or be @@ -3237,6 +3450,94 @@ type ConnectorDisconnectResult struct { Status ConnectorStatus `json:"status"` } +// Eligible account. +// Experimental: ConnectorDiscoveryAccount is part of an experimental API and may change or +// be removed. +type ConnectorDiscoveryAccount struct { + // Opaque account ID. + AccountID string `json:"accountId"` + // Account metadata. + AuthInfo ConnectorDiscoveryAuthInfo `json:"authInfo"` +} + +// Eligible accounts. +// Experimental: ConnectorDiscoveryAccountList is part of an experimental API and may change +// or be removed. +type ConnectorDiscoveryAccountList struct { + // Eligible accounts. + Accounts []ConnectorDiscoveryAccount `json:"accounts"` + // Availability. + Availability ConnectorDiscoveryAvailability `json:"availability"` +} + +// Selected account. +// Experimental: ConnectorDiscoveryAccountRequest is part of an experimental API and may +// change or be removed. +type ConnectorDiscoveryAccountRequest struct { + // Opaque account ID. + AccountID string `json:"accountId"` +} + +// Account metadata. +// Experimental: ConnectorDiscoveryAuthInfo is part of an experimental API and may change or +// be removed. +type ConnectorDiscoveryAuthInfo struct { + // Host. + Host string `json:"host"` + // Login. + Login string `json:"login"` + // Authentication type. + Type AuthInfoType `json:"type"` +} + +// Feature availability. +// Experimental: ConnectorDiscoveryCapabilities is part of an experimental API and may +// change or be removed. +type ConnectorDiscoveryCapabilities struct { + // API version. + APIVersion int64 `json:"apiVersion"` + // Availability. + Availability ConnectorDiscoveryAvailability `json:"availability"` + // Whether results are cached. + ConditionalCache bool `json:"conditionalCache"` + // Whether accounts are selected by opaque ID. + OpaqueAccountSelection bool `json:"opaqueAccountSelection"` +} + +// Entry. +// Experimental: ConnectorDiscoveryCatalogEntry is part of an experimental API and may +// change or be removed. +type ConnectorDiscoveryCatalogEntry struct { + // Description. + Description *string `json:"description,omitempty"` + // Display name. + DisplayName string `json:"displayName"` + // Logo. + Logo *string `json:"logo,omitempty"` + // Name. + Name string `json:"name"` + // Release tag. + ReleaseTag *string `json:"releaseTag,omitempty"` + // Status. + Status ConnectorCatalogStatus `json:"status"` + // Tier. + Tier *string `json:"tier,omitempty"` +} + +// Entries for the selected account. +// Experimental: ConnectorDiscoveryCatalogResult is part of an experimental API and may +// change or be removed. +type ConnectorDiscoveryCatalogResult struct { + // Opaque account ID. + AccountID string `json:"accountId"` + // Entries. + Connectors []ConnectorDiscoveryCatalogEntry `json:"connectors"` + // Refresh time in Unix epoch milliseconds. + RefreshedAtMs int64 `json:"refreshedAtMs"` + // Revision. + Revision int64 `json:"revision"` +} + // Requests authoritative Connector-to-MCP reconciliation for the pinned account. // Experimental: ConnectorReconcileRequest is part of an experimental API and may change or // be removed. @@ -3630,6 +3931,15 @@ type CurrentModel struct { // Latest unclaimed Auto preference waiting for a future user turn. Null means the pending // request is returning to provider-default routing. PendingAutoTier *AutoTier `json:"pendingAutoTier,omitempty"` + // 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. + PlanBaseModelID *string `json:"planBaseModelId,omitempty"` + // Provider of planBaseModelId, when that saved selection is provider-qualified. + PlanBaseProviderID *string `json:"planBaseProviderId,omitempty"` + // Provider selected for this model. Together with modelId, identifies a catalog entry even + // when providers serve the same model. Omitted for unattributed legacy selections. + ProviderID *string `json:"providerId,omitempty"` // 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. @@ -3656,6 +3966,33 @@ type CurrentToolMetadata struct { NamespacedName *string `json:"namespacedName,omitempty"` } +// Result of one customization reload component. +// Experimental: CustomizationReloadOutcome is part of an experimental API and may change or +// be removed. +type CustomizationReloadOutcome struct { + // Reason for a skipped component or description of a failure, when available + Detail *string `json:"detail,omitempty"` + // Whether the component reloaded, was skipped, or failed + Status CustomizationReloadStatus `json:"status"` + // Component whose reload was attempted or skipped + Subsystem CustomizationReloadSubsystem `json:"subsystem"` +} + +// 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: CustomizationsReloadResult is part of an experimental API and may change or +// be removed. +type CustomizationsReloadResult struct { + // Errors from any component that could not be refreshed + Errors []string `json:"errors"` + // Outcome of each component in reload order; a skipped component was not configured or + // loaded + Outcomes []CustomizationReloadOutcome `json:"outcomes"` + // Warnings from skill discovery + Warnings []string `json:"warnings"` +} + // A file included in the session debug bundle. // Experimental: DebugCollectLogsCollectedEntry is part of an experimental API and may // change or be removed. @@ -3997,6 +4334,44 @@ type DiscoveredMCPServer struct { Type *DiscoveredMCPServerType `json:"type,omitempty"` } +// 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: DiscoveredModel is part of an experimental API and may change or be removed. +type DiscoveredModel struct { + // Provider-reported model capabilities. Omitted capability fields are unknown; explicit + // false values are preserved. + Capabilities ModelCapabilities `json:"capabilities"` + // Provider-reported model artifact details. + Details ModelArtifactDetails `json:"details"` + // Provider-reported artifact digest. + Digest *string `json:"digest,omitempty"` + // Provider-native model identifier. + ID string `json:"id"` + // Provider-reported last-modified timestamp. + ModifiedAt *time.Time `json:"modifiedAt,omitempty"` + // Provider-reported display name. + Name *string `json:"name,omitempty"` + // Attribution for the adapter that produced this model row. + Provenance ModelProviderProvenance `json:"provenance"` + // Provider-reported artifact size in bytes. + SizeBytes *int64 `json:"sizeBytes,omitempty"` + // Non-fatal warnings encountered while enriching this model. + Warnings []ModelProviderWarning `json:"warnings"` +} + +// 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: DiscoveredModelList is part of an experimental API and may change or be +// removed. +type DiscoveredModelList struct { + // Provider-native models in provider order. + Models []DiscoveredModel `json:"models"` + // Typed operation outcome. + Outcome ModelProviderOperationOutcome `json:"outcome"` +} + // Slash-prefixed command string to enqueue for FIFO processing. // Experimental: EnqueueCommandParams is part of an experimental API and may change or be // removed. @@ -4040,66 +4415,6 @@ func (UnsupportedEnqueueCommandResult) Queued() bool { return false } -// OneAuth token request supplied by a trusted host application. -// Experimental: EntraTokenAcquireRequest is part of an experimental API and may change or -// be removed. -type EntraTokenAcquireRequest struct { - // Previously rejected token that OneAuth must bypass during renewal. - AccessTokenToRenew *string `json:"accessTokenToRenew,omitempty"` - // Public client application id. - ClientID string `json:"clientId"` - // Whether the broker may show interaction. - Interaction EntraTokenInteraction `json:"interaction"` - // Broker redirect URI registered for the client. Required: the OneAuth broker validates a - // non-empty, registered redirect URI for the public client (MSAL broker registration), so - // this is not a browser-flow vestige and cannot be omitted. - RedirectURI string `json:"redirectUri"` - // Exact delegated scopes to request. - Scopes []string `json:"scopes"` - // Tenant id or tenant selector, such as common or organizations. - TenantID string `json:"tenantId"` -} - -// Result of a OneAuth token acquisition. -// Experimental: EntraTokenAcquireResult is part of an experimental API and may change or be -// removed. -type EntraTokenAcquireResult interface { - entraTokenAcquireResult() - Status() EntraTokenAcquireResultStatus -} - -type RawEntraTokenAcquireResultData struct { - Discriminator EntraTokenAcquireResultStatus - Raw json.RawMessage -} - -func (RawEntraTokenAcquireResultData) entraTokenAcquireResult() {} -func (r RawEntraTokenAcquireResultData) Status() EntraTokenAcquireResultStatus { - return r.Discriminator -} - -type EntraTokenAcquireResultInteractionRequired struct { -} - -func (EntraTokenAcquireResultInteractionRequired) entraTokenAcquireResult() {} -func (EntraTokenAcquireResultInteractionRequired) Status() EntraTokenAcquireResultStatus { - return EntraTokenAcquireResultStatusInteractionRequired -} - -type EntraTokenAcquireResultOk struct { - // Opaque access token. - AccessToken string `json:"accessToken"` - // Opaque OneAuth account id, when supplied by the broker. - AccountID *string `json:"accountId,omitempty"` - // Expiry as milliseconds since Unix epoch, when supplied by OneAuth. - ExpiresOnTimestamp *float64 `json:"expiresOnTimestamp,omitempty"` -} - -func (EntraTokenAcquireResultOk) entraTokenAcquireResult() {} -func (EntraTokenAcquireResultOk) Status() EntraTokenAcquireResultStatus { - return EntraTokenAcquireResultStatusOk -} - // Hosting capabilities and session capacity advertised by an environment. // Experimental: EnvironmentCapabilities is part of an experimental API and may change or be // removed. @@ -4701,6 +5016,26 @@ type FolderTrustCheckResult struct { Trusted bool `json:"trusted"` } +// The remote the checked-out branch tracks. +// Experimental: GitCurrentBranchRemoteResult is part of an experimental API and may change +// or be removed. +// Internal: GitCurrentBranchRemoteResult is an internal SDK API and is not part of the +// public surface. +type GitCurrentBranchRemoteResult struct { + // 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. + Remote string `json:"remote"` +} + +// Working-tree path a git query applies to. +// Experimental: GitCwdRequest is part of an experimental API and may change or be removed. +// Internal: GitCwdRequest is an internal SDK API and is not part of the public surface. +type GitCwdRequest struct { + // Absolute path to a directory inside the git working tree to query. + Cwd string `json:"cwd"` +} + // Safe discovery information. Host-side relay bootstrap credentials are never included. // Experimental: GitHubEnvironment is part of an experimental API and may change or be // removed. @@ -4727,6 +5062,91 @@ type GitHubEnvironment struct { Status string `json:"status"` } +// A GitHub login the authenticated user may act as: their own account, or an organization +// they belong to. +// Experimental: GitHubOwnerOption is part of an experimental API and may change or be +// removed. +// Internal: GitHubOwnerOption is an internal SDK API and is not part of the public surface. +type GitHubOwnerOption struct { + // The owner's GitHub login. + Login string `json:"login"` + // Which kind of owner this is. The authenticated user's own account is always reported as + // `user`. + Type string `json:"type"` +} + +// The owner listing to abandon. +// Experimental: GitHubOwnersCancelRequest is part of an experimental API and may change or +// be removed. +// Internal: GitHubOwnersCancelRequest is an internal SDK API and is not part of the public +// surface. +type GitHubOwnersCancelRequest struct { + // Request id the listing was started with. + RequestID int64 `json:"requestId"` +} + +// Whether the id named a running owner listing. +// Experimental: GitHubOwnersCancelResult is part of an experimental API and may change or +// be removed. +// Internal: GitHubOwnersCancelResult is an internal SDK API and is not part of the public +// surface. +type GitHubOwnersCancelResult struct { + // 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. + Canceled bool `json:"canceled"` +} + +// Credential to list owners under, and the request id that makes the listing cancellable. +// Experimental: GitHubOwnersListRequest is part of an experimental API and may change or be +// removed. +// Internal: GitHubOwnersListRequest is an internal SDK API and is not part of the public +// surface. +type GitHubOwnersListRequest struct { + // 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. + AuthInfo any `json:"authInfo"` + // 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. + RequestID int64 `json:"requestId"` +} + +// 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: GitHubOwnersListResult is part of an experimental API and may change or be +// removed. +// Internal: GitHubOwnersListResult is an internal SDK API and is not part of the public +// surface. +type GitHubOwnersListResult struct { + // 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. + Message *string `json:"message,omitempty"` + // The owners, on success: the authenticated user first, then the organizations they belong + // to. + // Internal: Owners is part of the SDK's internal API surface and is not intended for + // external use. + Owners []GitHubOwnerOption `json:"owners,omitzero"` + // 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. + ThrowError *string `json:"throwError,omitempty"` + // A line the caller should log. Present only alongside `message`, and only for failures + // worth recording. + Warning *string `json:"warning,omitempty"` +} + +// 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: GitHubOwnersRequestIDResult is part of an experimental API and may change +// or be removed. +type GitHubOwnersRequestIDResult struct { + // Request id to pass to `gitHubOwners.list` and, to abandon it, `gitHubOwners.cancel`. + RequestID int64 `json:"requestId"` +} + // Pointer to a GitHub repository. // Experimental: GitHubRepoRef is part of an experimental API and may change or be removed. type GitHubRepoRef struct { @@ -4738,6 +5158,39 @@ type GitHubRepoRef struct { Owner string `json:"owner"` } +// Working-tree path whose owning GitHub repository should be resolved. +// Experimental: GitHubRepositoryAtPathRequest is part of an experimental API and may change +// or be removed. +// Internal: GitHubRepositoryAtPathRequest is an internal SDK API and is not part of the +// public surface. +type GitHubRepositoryAtPathRequest struct { + // Absolute path to a directory inside the git working tree to resolve. + Path string `json:"path"` +} + +// The GitHub repository that owns the requested path, when the selected remote (`origin`, +// else the first) is on a GitHub host. +// Experimental: GitHubRepositoryAtPathResult is part of an experimental API and may change +// or be removed. +// Internal: GitHubRepositoryAtPathResult is an internal SDK API and is not part of the +// public surface. +type GitHubRepositoryAtPathResult struct { + // Resolved repository identity, or null when the selected remote resolves to no GitHub host. + Repository *GitHubRepositoryIdentity `json:"repository,omitempty"` +} + +// Owner, name, and host of a GitHub repository, as resolved from a git remote URL. +// Experimental: GitHubRepositoryIdentity is part of an experimental API and may change or +// be removed. +type GitHubRepositoryIdentity struct { + // Host the remote points at, for example `github.com` or a GitHub Enterprise hostname. + Host string `json:"host"` + // Repository name, without the owner prefix or the `.git` suffix. + Name string `json:"name"` + // Repository owner login (user or organization). + Owner string `json:"owner"` +} + // Client environment metadata describing the process that produced a telemetry event. // Experimental: GitHubTelemetryClientInfo is part of an experimental API and may change or // be removed. @@ -4874,6 +5327,142 @@ func (GitHubTokenAcquireResultToken) Kind() GitHubTokenAcquireResultKind { return GitHubTokenAcquireResultKindToken } +// A GitHub repository one of a working tree's remotes points at. +// Experimental: GitRemoteRepository is part of an experimental API and may change or be +// removed. +// Internal: GitRemoteRepository is an internal SDK API and is not part of the public +// surface. +type GitRemoteRepository struct { + // GitHub host serving the repository, which is not `github.com` for a GitHub Enterprise + // remote. + Host string `json:"host"` + // Repository name, without the owner. + Name string `json:"name"` + // Account or organization owning the repository. + Owner string `json:"owner"` + // Name of the first remote that produced this distinct repository entry, such as `origin` + // or `upstream`. + RemoteName string `json:"remoteName"` +} + +// Git working tree whose GitHub remotes should be listed. +// Experimental: GitReposFromRemotesRequest is part of an experimental API and may change or +// be removed. +// Internal: GitReposFromRemotesRequest is an internal SDK API and is not part of the public +// surface. +type GitReposFromRemotesRequest struct { + // Absolute path to the root of the git working tree. + GitRoot string `json:"gitRoot"` +} + +// The GitHub repositories a working tree's remotes point at. +// Experimental: GitReposFromRemotesResult is part of an experimental API and may change or +// be removed. +// Internal: GitReposFromRemotesResult is an internal SDK API and is not part of the public +// surface. +type GitReposFromRemotesResult struct { + // 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. + // Internal: Repositories is part of the SDK's internal API surface and is not intended for + // external use. + Repositories []GitRemoteRepository `json:"repositories"` +} + +// Selects the configuration directory whose machine-wide state to read. +// Experimental: GlobalStateLoadForConfigDirRequest is part of an experimental API and may +// change or be removed. +// Internal: GlobalStateLoadForConfigDirRequest is an internal SDK API and is not part of +// the public surface. +type GlobalStateLoadForConfigDirRequest struct { + // 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. + ConfigDir *string `json:"configDir,omitempty"` +} + +// 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: GlobalStateLoadResult is part of an experimental API and may change or be +// removed. +// Internal: GlobalStateLoadResult is an internal SDK API and is not part of the public +// surface. +type GlobalStateLoadResult struct { + // Whether the user has answered the prompt suggesting they install the desktop app. + AppInstallNudgeResponded *bool `json:"appInstallNudgeResponded,omitempty"` + // Whether the app tip has been shown. + AppTipShown *bool `json:"appTipShown,omitempty"` + // Terminals the user has already been asked to set up, so the host does not ask twice. + AskedSetupTerminals []string `json:"askedSetupTerminals,omitzero"` + // 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. + AutoFeedbackLastPromptedAt *string `json:"autoFeedbackLastPromptedAt,omitempty"` + // When the host first ran on this machine. + FirstLaunchAt *string `json:"firstLaunchAt,omitempty"` + // Plugins installed on this machine. + InstalledPlugins []InstalledPlugin `json:"installedPlugins,omitzero"` + // Account used for the most recent sign-in. + // Internal: LastLoggedInUser is part of the SDK's internal API surface and is not intended + // for external use. + LastLoggedInUser *LoggedInUser `json:"lastLoggedInUser,omitempty"` + // Every account the host has signed in to on this machine. + // Internal: LoggedInUsers is part of the SDK's internal API surface and is not intended for + // external use. + LoggedInUsers []LoggedInUser `json:"loggedInUsers,omitzero"` + // Whether the one-off cleanup of stored reasoning summaries has run. + ReasoningSummariesCleanupDone *bool `json:"reasoningSummariesCleanupDone,omitempty"` + // Models the user selected recently, most recent first. + RecentModelIDs []string `json:"recentModelIds,omitzero"` + // Whether the user declined to trust the sandbox credential proxy CA. + SandboxCredentialProxyCaDeclined *bool `json:"sandboxCredentialProxyCaDeclined,omitempty"` + // Whether the sandbox onboarding has been shown. + SandboxOnboardingShown *bool `json:"sandboxOnboardingShown,omitempty"` + // Whether the user is a GitHub or Microsoft staff member, which unlocks internal-only + // behavior. + Staff *bool `json:"staff,omitempty"` + // Whether the user was recognized as GitHub staff. + StaffGitHub *bool `json:"staffGithub,omitempty"` + // When the staff-only log level migration last ran. + StaffLogLevelMigrationAt *string `json:"staffLogLevelMigrationAt,omitempty"` + // Whether the user was recognized as Microsoft staff. + StaffMicrosoft *bool `json:"staffMicrosoft,omitempty"` + // When the staff-only model reset last ran. + StaffModelResetAt *string `json:"staffModelResetAt,omitempty"` + // When the staff-only update channel migration last ran. + StaffUpdateChannelMigrationAt *string `json:"staffUpdateChannelMigrationAt,omitempty"` + // Folders where the user declined the init prompt, so it stays hidden there. + SuppressInitFolders []string `json:"suppressInitFolders,omitzero"` + // Folders the user has marked as trusted. + TrustedFolders []string `json:"trustedFolders,omitzero"` +} + +// 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: GlobalStateWriteKeyRequest is part of an experimental API and may change or +// be removed. +// Internal: GlobalStateWriteKeyRequest is an internal SDK API and is not part of the public +// surface. +type GlobalStateWriteKeyRequest struct { + // 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. + ConfigDir *string `json:"configDir,omitempty"` + // 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. + Key string `json:"key"` + // Value to store for the key. Omit it, or pass null, to remove the key instead. + Value any `json:"value,omitempty"` +} + +// Experimental: GlobalStateWriteKeyResult is part of an experimental API and may change or +// be removed. +type GlobalStateWriteKeyResult struct { +} + // Pending external tool call request ID, with the tool result or an error describing why it // failed. // Experimental: HandlePendingToolCallRequest is part of an experimental API and may change @@ -5316,6 +5905,28 @@ type HostGitHubEnvironmentOptions struct { ComputeID string `json:"computeId"` // Human-readable environment display name. Name string `json:"name"` + // 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. + RequireConnectionBinding *bool `json:"requireConnectionBinding,omitempty"` +} + +// Reads a running host's catalog or the runtime's saved default catalog. +// Experimental: HostListSessionsRequest is part of an experimental API and may change or be +// removed. +type HostListSessionsRequest struct { + // Listener UUID returned by host.start; the caller must own it. Omit to read + // the saved default compute-identity catalog without starting a listener. + HostID *string `json:"hostId,omitempty"` +} + +// The complete live and dormant advertised catalog, without subscribing to sessions. +// Experimental: HostListSessionsResult is part of an experimental API and may change or be +// removed. +type HostListSessionsResult struct { + // Complete advertised session summaries, in the host's catalog order. + Sessions []HostSessionSummary `json:"sessions"` } // Normalized local WebSocket listener settings. @@ -5410,6 +6021,8 @@ type HostRegisterSessionRequest struct { } // The existing runtime identity and its resource on the listener. +// Experimental: HostRegisterSessionResult is part of an experimental API and may change or +// be removed. type HostRegisterSessionResult struct { // Canonical runtime ID of the published session. SessionID string `json:"sessionId"` @@ -5445,6 +6058,9 @@ type HostSessionCreateRequest struct { Config map[string]any `json:"config"` // Unique identity for this participation, independent of the session lifetime. HandoffID string `json:"handoffId"` + // For an explicit publication, borrow an existing owning-connection resident + // instead of invoking the resume factory. Cold sessions still use the factory. + PreferResident *bool `json:"preferResident,omitempty"` // Resume an app-owned durable session instead of creating a new session. Resume *bool `json:"resume,omitempty"` } @@ -5480,6 +6096,25 @@ type HostSessionReleaseRequest struct { HandoffID string `json:"handoffId"` } +// Metadata advertised by the AHP host, not a guarantee that history can be resumed. +// Experimental: HostSessionSummary is part of an experimental API and may change or be +// removed. +type HostSessionSummary struct { + // Optional activity description advertised by the host. + Activity *string `json:"activity,omitempty"` + // Session creation time as an ISO 8601 timestamp. + CreatedAt string `json:"createdAt"` + // Last modification time as an ISO 8601 timestamp. + ModifiedAt string `json:"modifiedAt"` + // Stable AHP resource URI identifying the session on this host. + Resource string `json:"resource"` + // Unsigned 32-bit AHP session status bitset (0..=4294967295); unknown bits must be + // preserved. + Status int64 `json:"status"` + // Authoritative title advertised by the host. + Title string `json:"title"` +} + // Empty acknowledgement for a completed host lifecycle operation. // Experimental: HostShutdownResult is part of an experimental API and may change or be // removed. @@ -5490,6 +6125,9 @@ type HostShutdownResult struct { // Experimental: HostStartRequest is part of an experimental API and may change or be // removed. type HostStartRequest struct { + // Stable application identity selecting its durable host catalog. Defaults to the persisted + // runtime compute GUID; must agree with githubEnvironment.computeId when both are supplied. + ComputeID *string `json:"computeId,omitempty"` // Registers a GitHub Mission Control environment and enables its relay transport. GitHubEnvironment *HostGitHubEnvironmentOptions `json:"githubEnvironment,omitempty"` // Caller-generated UUID identifying this connection-owned listener. @@ -6074,6 +6712,22 @@ type LocalSessionMetadataValue struct { Summary *string `json:"summary,omitempty"` } +// 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: LoggedInUser is part of an experimental API and may change or be removed. +// Internal: LoggedInUser is an internal SDK API and is not part of the public surface. +type LoggedInUser struct { + // Source account this account was derived from, when one was recorded. + DerivedFrom *string `json:"derivedFrom,omitempty"` + // Host the account belongs to, such as `github.com` or an Enterprise server. + Host string `json:"host"` + // Account kind, when the host recorded one. Consumers must tolerate new strings. + Kind *string `json:"kind,omitempty"` + // Account login on that host. + Login string `json:"login"` +} + // Message text, optional severity level, persistence flag, optional follow-up URL, and // optional tip. // Experimental: LogRequest is part of an experimental API and may change or be removed. @@ -6133,6 +6787,18 @@ type ManagedMCPServerConfig struct { URL string `json:"url"` } +// Retry outcome for one plugin required by managed settings. +// Experimental: ManagedPluginRetryEntry is part of an experimental API and may change or be +// removed. +type ManagedPluginRetryEntry struct { + // Why the plugin was not prepared. Present when `status` is `failed` or `deferred`. + Error *string `json:"error,omitempty"` + // Plugin spec in "plugin@marketplace" form. + Spec string `json:"spec"` + // What the retry did for this plugin. + Status ManagedPluginRetryStatus `json:"status"` +} + // Lock state and provenance of one managed setting. // Experimental: ManagedSettingMeta is part of an experimental API and may change or be // removed. @@ -6140,6 +6806,9 @@ type ManagedSettingMeta struct { // Whether users and repositories may choose a different value. `false` means policy locks // the value. Overridable bool `json:"overridable"` + // Original managed value when the runtime adjusted it to a supported effective value. + // Omitted when no adjustment was needed. + Requested *string `json:"requested,omitempty"` // 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. @@ -6229,6 +6898,10 @@ type ManagedSettingsLayer struct { type ManagedSettingsMeta struct { // Lock state and provenance of `values.autoTier`. AutoTier *ManagedSettingMeta `json:"autoTier,omitempty"` + // Lock state and provenance of `values.contextTier`. + ContextTier *ManagedSettingMeta `json:"contextTier,omitempty"` + // Lock state and provenance of `values.effortLevel`. + EffortLevel *ManagedSettingMeta `json:"effortLevel,omitempty"` // Lock state and provenance of `values.model`. Model *ManagedSettingMeta `json:"model,omitempty"` } @@ -6301,6 +6974,9 @@ type ManagedSettingsResolveRequest struct { // Opaque account identifier returned by `account.getAllUsers`. When omitted, the current // account is used, or device policy only when no account is signed in. SelectionID *string `json:"selectionId,omitempty"` + // Working directory used to run an organization policy helper. When omitted, sessionless + // resolution does not run the helper. + WorkingDirectory *string `json:"workingDirectory,omitempty"` } // Effective enterprise managed settings for an account, resolved without a session. @@ -6378,8 +7054,14 @@ type ManagedSettingsValidateResult struct { type ManagedSettingsValues struct { // Managed Auto routing preference, used when the selected model is `auto`. AutoTier *AutoTier `json:"autoTier,omitempty"` - // Managed default model identifier, as configured. New sessions start with it; it can name - // a model the account cannot use, so hosts match it against the listed models. + // Managed context-tier default for the managed concrete model. + ContextTier *ContextTier `json:"contextTier,omitempty"` + // Managed reasoning-effort default for the managed concrete model. The runtime clamps it to + // an entitled effort when model availability is known. + EffortLevel *string `json:"effortLevel,omitempty"` + // 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. Model *string `json:"model,omitempty"` } @@ -6778,6 +7460,48 @@ type MCPConfigUpdateRequest struct { type MCPConfigUpdateResult struct { } +// Effective MCP configuration entry. Configuration enablement is distinct from the optional +// live observation. +// Experimental: MCPConfiguredServer is part of an experimental API and may change or be +// removed. +type MCPConfiguredServer struct { + // Human-readable display name supplied by configuration. + DisplayName *string `json:"displayName,omitempty"` + // Whether this configured server is enabled after session configuration and policy + // filtering. + Enabled bool `json:"enabled"` + // Observed state from an already materialized matching server. Omitted when no live graph + // has this configured server; it never determines configuration enablement. + Live MCPConfiguredServerState `json:"live"` + // Server name (config key) + Name string `json:"name"` + // Configuration provenance: user, workspace, plugin, builtin, or managed. + Source *MCPServerSource `json:"source,omitempty"` + // Plugin name that provided this server, when source is plugin. + SourcePlugin *string `json:"sourcePlugin,omitempty"` + // Plugin version that provided this server, when source is plugin. + SourcePluginVersion *string `json:"sourcePluginVersion,omitempty"` +} + +// Effective MCP configuration with optional live observations from matching already +// materialized servers. +// Experimental: MCPConfiguredServerList is part of an experimental API and may change or be +// removed. +type MCPConfiguredServerList struct { + // Effective configured MCP servers. + Servers []MCPConfiguredServer `json:"servers"` +} + +// Observational state for a matching already materialized MCP server. +// Experimental: MCPConfiguredServerState is part of an experimental API and may change or +// be removed. +type MCPConfiguredServerState struct { + // Observed connection error, when the materialized server failed. + Error *string `json:"error,omitempty"` + // Observed connection status. This is not a configuration or readiness guarantee. + Status MCPServerStatus `json:"status"` +} + // Credential-free authentication identity used to configure GitHub MCP. // Experimental: MCPConfigureGitHubRequest is part of an experimental API and may change or // be removed. @@ -7636,6 +8360,18 @@ type MCPOauthCancelLoginResult struct { Cancelled bool `json:"cancelled"` } +// Host-delivered callback for a runtime-managed MCP OAuth login. +// Experimental: MCPOauthCompleteRequest is part of an experimental API and may change or be +// removed. +type MCPOauthCompleteRequest struct { + // Opaque identifier returned by session.mcp.oauth.login for the pending external callback. + AuthorizationID string `json:"authorizationId"` + // 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. + CallbackURL string `json:"callbackUrl"` +} + // Pending MCP OAuth request ID and host-provided token or cancellation response. // Experimental: MCPOauthHandlePendingRequest is part of an experimental API and may change // or be removed. @@ -7656,7 +8392,7 @@ type MCPOauthHandlePendingResult struct { } // Remote MCP server name and optional overrides controlling reauthentication, OAuth client -// display name, callback success-page copy, and static OAuth client selection. +// display name, callback handling, and static OAuth client selection. // Experimental: MCPOauthLoginRequest is part of an experimental API and may change or be // removed. type MCPOauthLoginRequest struct { @@ -7693,6 +8429,12 @@ type MCPOauthLoginRequest struct { // runtime treats it as confidential and uses the per-login clientSecret if provided, // otherwise retrieving the client secret from the MCP OAuth secret store. PublicClient *bool `json:"publicClient,omitempty"` + // 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. + RedirectURI *string `json:"redirectUri,omitempty"` // Name of the remote MCP server to authenticate ServerName string `json:"serverName"` } @@ -7702,11 +8444,16 @@ type MCPOauthLoginRequest struct { // Experimental: MCPOauthLoginResult is part of an experimental API and may change or be // removed. type MCPOauthLoginResult struct { + // 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. + AuthorizationID *string `json:"authorizationId,omitempty"` // 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. When present, the runtime starts the callback listener before - // returning and continues the flow in the background; completion is signaled via - // session.mcp_server_status_changed. + // 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. AuthorizationURL *string `json:"authorizationUrl,omitempty"` // Runtime-issued owned flow identity; never a server name or installation operation ID. LoginID *string `json:"loginId,omitempty"` @@ -8359,6 +9106,122 @@ type MCPPrepareInstallRequest struct { Source MCPServerCardReference `json:"source"` } +// An MCP prompt descriptor. Server-provided non-standard fields are exposed under +// `additionalProperties`. +// Experimental: MCPPrompt is part of an experimental API and may change or be removed. +type MCPPrompt struct { + // Server-provided non-standard descriptor fields + AdditionalProperties map[string]any `json:"additionalProperties,omitzero"` + // Arguments accepted by the prompt + Arguments []MCPPromptArgument `json:"arguments,omitzero"` + // Description of what this prompt provides + Description *string `json:"description,omitempty"` + // Icons associated with this prompt + Icons []MCPPromptIcon `json:"icons,omitzero"` + // Prompt-level metadata + Meta map[string]any `json:"_meta,omitzero"` + // The programmatic name of the prompt + Name string `json:"name"` + // Human-readable display title + Title *string `json:"title,omitempty"` +} + +// An argument accepted by an MCP prompt. +// Experimental: MCPPromptArgument is part of an experimental API and may change or be +// removed. +type MCPPromptArgument struct { + // Server-provided non-standard argument fields + AdditionalProperties map[string]any `json:"additionalProperties,omitzero"` + // Description of the argument + Description *string `json:"description,omitempty"` + // Argument-level metadata + Meta map[string]any `json:"_meta,omitzero"` + // Name of the argument + Name string `json:"name"` + // Whether the argument is required; omission is distinct from false + Required *bool `json:"required,omitempty"` +} + +// An MCP prompt icon with standard size hints and preserved non-standard fields. +// Experimental: MCPPromptIcon is part of an experimental API and may change or be removed. +type MCPPromptIcon struct { + // Server-provided non-standard icon fields + AdditionalProperties map[string]any `json:"additionalProperties,omitzero"` + // Icon MIME type, when known + MIMEType *string `json:"mimeType,omitempty"` + // Icon sizes, such as `48x48` or `any` + Sizes []string `json:"sizes,omitzero"` + // Icon URI + Src string `json:"src"` + // Theme hint for this icon + Theme *string `json:"theme,omitempty"` +} + +// An MCP prompt message with opaque JSON content preserved without flattening or +// content-type filtering. +// Experimental: MCPPromptMessage is part of an experimental API and may change or be +// removed. +type MCPPromptMessage struct { + // Server-provided non-standard message fields + AdditionalProperties map[string]any `json:"additionalProperties,omitzero"` + // The original MCP content block, including nested metadata and unfamiliar content types + Content any `json:"content"` + // Message-level metadata + Meta map[string]any `json:"_meta,omitzero"` + // The role of the message sender + Role MCPPromptRole `json:"role"` +} + +// MCP server, prompt name, and optional string-valued arguments. +// Experimental: MCPPromptsGetRequest is part of an experimental API and may change or be +// removed. +type MCPPromptsGetRequest struct { + // String-valued arguments to pass to the prompt + Arguments map[string]string `json:"arguments,omitzero"` + // The programmatic name of the prompt + PromptName string `json:"promptName"` + // Name of the MCP server hosting the prompt + ServerName string `json:"serverName"` +} + +// Prompt messages returned by the MCP server without sending them to the model. +// Experimental: MCPPromptsGetResult is part of an experimental API and may change or be +// removed. +type MCPPromptsGetResult struct { + // Server-provided non-standard result fields + AdditionalProperties map[string]any `json:"additionalProperties,omitzero"` + // Description of the prompt + Description *string `json:"description,omitempty"` + // Ordered prompt messages + Messages []MCPPromptMessage `json:"messages"` + // MCP result metadata + Meta map[string]any `json:"_meta,omitzero"` +} + +// MCP server whose prompts to enumerate. +// Experimental: MCPPromptsListRequest is part of an experimental API and may change or be +// removed. +type MCPPromptsListRequest struct { + // Opaque MCP pagination cursor from a prior `nextCursor` value + Cursor *string `json:"cursor,omitempty"` + // Name of the MCP server whose prompts to enumerate + ServerName string `json:"serverName"` +} + +// One page of prompts advertised by the named MCP server. +// Experimental: MCPPromptsListResult is part of an experimental API and may change or be +// removed. +type MCPPromptsListResult struct { + // Server-provided non-standard result fields + AdditionalProperties map[string]any `json:"additionalProperties,omitzero"` + // MCP result metadata + Meta map[string]any `json:"_meta,omitzero"` + // Opaque cursor for the next page, if the server has more prompts + NextCursor *string `json:"nextCursor,omitempty"` + // Prompts advertised by the server + Prompts []MCPPrompt `json:"prompts"` +} + // Registration parameters for an external MCP client. // Experimental: MCPRegisterExternalClientRequest is part of an experimental API and may // change or be removed. @@ -8814,6 +9677,9 @@ type MCPServer struct { // Connection status: connected, failed, needs-auth, pending, disabled, stopped, or // not_configured Status MCPServerStatus `json:"status"` + // Configured URL for an HTTP/SSE server, regardless of configuration source. Omitted for + // local and in-memory servers. + URL *string `json:"url,omitempty"` } // Set to `true` to use defaults, or provide an object with additional auth or OIDC settings. @@ -9198,10 +10064,15 @@ type MetadataContextHeaviestMessagesResult struct { // Experimental: MetadataContextInfoRequest is part of an experimental API and may change or // be removed. type MetadataContextInfoRequest struct { - // Maximum output tokens allowed by the target model. Pass 0 if unknown. + // 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. OutputTokenLimit int64 `json:"outputTokenLimit"` - // Maximum prompt tokens allowed by the target model. Pass 0 to use the runtime default. + // Advertised prompt allowance. Pass 0 to resolve the selected model and context tier from + // the session. PromptTokenLimit int64 `json:"promptTokenLimit"` + // Provider owning selectedModel. Omit to use the current provider for the current model, or + // deterministic legacy lookup for a different model. + ProviderID *string `json:"providerId,omitempty"` // Model identifier used for tokenization. Omit to use the session default. Used both for // token counting and to compute display values. SelectedModel *string `json:"selectedModel,omitempty"` @@ -9390,6 +10261,11 @@ type Model struct { SupportedContextTiers []string `json:"supportedContextTiers,omitzero"` // Supported reasoning effort levels (only present if model supports reasoning effort) SupportedReasoningEfforts []string `json:"supportedReasoningEfforts,omitzero"` + // 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. + Vendor *string `json:"vendor,omitempty"` // 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. @@ -9431,12 +10307,35 @@ type ModelApplyStartupOverlayRequest struct { RepoContextTier *string `json:"repoContextTier,omitempty"` // Model selected by repository settings, when configured. RepoModel *string `json:"repoModel,omitempty"` + // Provider owning the repository-selected model. Omitted repository provider identity + // retains legacy bare-model behavior. + RepoModelProviderID *string `json:"repoModelProviderId,omitempty"` // Reasoning effort selected by repository settings, when configured. RepoReasoningEffort *string `json:"repoReasoningEffort,omitempty"` // Model required by server-managed policy, when configured. ServerManagedModel *string `json:"serverManagedModel,omitempty"` } +// Provider-reported model artifact metadata. +// Experimental: ModelArtifactDetails is part of an experimental API and may change or be +// removed. +type ModelArtifactDetails struct { + // Provider-reported model architecture. + Architecture *string `json:"architecture,omitempty"` + // Provider-reported model families. + Families []string `json:"families,omitzero"` + // Primary model family. + Family *string `json:"family,omitempty"` + // Artifact format, such as `gguf`. + Format *string `json:"format,omitempty"` + // Provider-reported parameter count label. + ParameterSize *string `json:"parameterSize,omitempty"` + // Provider-reported quantization label. + Quantization *string `json:"quantization,omitempty"` + // Provider-reported tokenizer. + Tokenizer *string `json:"tokenizer,omitempty"` +} + // Billing information // Experimental: ModelBilling is part of an experimental API and may change or be removed. type ModelBilling struct { @@ -9632,12 +10531,33 @@ type ModelCapabilitiesSupports struct { AdaptiveThinking *AdaptiveThinkingSupport `json:"adaptive_thinking,omitempty"` // Whether this model supports reasoning effort configuration ReasoningEffort *bool `json:"reasoningEffort,omitempty"` + // Whether the model supports provider-native thinking. Independent of configurable + // reasoning effort; omission means unknown. + Thinking *bool `json:"thinking,omitempty"` // Whether this model supports canonical tool calling ToolCalls *bool `json:"toolCalls,omitempty"` // Whether this model supports vision/image input Vision *bool `json:"vision,omitempty"` } +// Experimental: ModelClearStartupSeedRequest is part of an experimental API and may change +// or be removed. +// Internal: ModelClearStartupSeedRequest is an internal SDK API and is not part of the +// public surface. +type ModelClearStartupSeedRequest struct { + ExpectedModel string `json:"expectedModel"` + // Provider captured with the startup seed; omission matches only an unqualified seed. + ExpectedProviderID *string `json:"expectedProviderId,omitempty"` +} + +// Experimental: ModelClearStartupSeedResult is part of an experimental API and may change +// or be removed. +// Internal: ModelClearStartupSeedResult is an internal SDK API and is not part of the +// public surface. +type ModelClearStartupSeedResult struct { + Cleared bool `json:"cleared"` +} + // 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. @@ -9695,6 +10615,106 @@ type ModelPolicy struct { Terms *string `json:"terms,omitempty"` } +// Normalized model-provider adapter definitions available to the session, not discovered +// instances. +// Experimental: ModelProviderAdapterCatalog is part of an experimental API and may change +// or be removed. +type ModelProviderAdapterCatalog struct { + // Available provider adapters ordered by adapterId. + Providers []ModelProviderAdapterDescriptor `json:"providers"` +} + +// A normalized model-provider adapter in the session's effective catalog. +// Experimental: ModelProviderAdapterDescriptor is part of an experimental API and may +// change or be removed. +type ModelProviderAdapterDescriptor struct { + // Stable opaque identity for routing to this adapter. Unique in the effective catalog, + // independent of live registration generations. + AdapterID string `json:"adapterId"` + // Adapter-declared policy for passive and automatic discovery. + AutomaticDiscovery ModelProviderAutomaticDiscoveryPolicy `json:"automaticDiscovery"` + // Human-readable provider name. + DisplayName string `json:"displayName"` + // Operations supported by this provider adapter. + Operations []ModelProviderAdapterOperationDescriptor `json:"operations"` + // Attribution for the adapter itself. + Provenance ModelProviderAttribution `json:"provenance"` + // Descriptive provider family, such as `ollama`. Different adapters may have the same + // family; use adapterId for routing. + ProviderKind string `json:"providerKind"` +} + +// An operation supported by a model-provider adapter. +// Experimental: ModelProviderAdapterOperationDescriptor is part of an experimental API and +// may change or be removed. +type ModelProviderAdapterOperationDescriptor struct { + // 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. + InputSchema any `json:"inputSchema,omitempty"` + // Supported operation name: `discover`, `getStatus`, or `models.list`. Unknown names and + // duplicate declarations are rejected. + Name string `json:"name"` +} + +// Contributor attribution, independent of routing identity and authorization. +// Experimental: ModelProviderAttribution is part of an experimental API and may change or +// be removed. +type ModelProviderAttribution struct { + // Human-readable contributor name, not the adapter display name. + OwnerDisplayName *string `json:"ownerDisplayName,omitempty"` + // Stable contributor identifier. Required and nonblank for extension and custom sources; + // optional for built-in and configured sources. Does not grant authority. + OwnerID *string `json:"ownerId,omitempty"` + // Kind of component that supplied the adapter. Attribution does not confer authority. + Source ModelProviderProvenanceSource `json:"source"` +} + +// Adapter-declared policy that tells clients whether discovery may run automatically. +// Experimental: ModelProviderAutomaticDiscoveryPolicy is part of an experimental API and +// may change or be removed. +type ModelProviderAutomaticDiscoveryPolicy struct { + // Whether automatic discovery is allowed, limited to configured providers, or explicit-only. + Mode ModelProviderAutomaticDiscoveryMode `json:"mode"` + // Maximum network scope used by this adapter during discovery. + NetworkScope ModelProviderDiscoveryNetworkScope `json:"networkScope"` + // 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. + RequiresInput bool `json:"requiresInput"` + // True when the adapter must be enabled by a trusted owner, such as a trusted extension, + // before automatic discovery may run. + RequiresTrust bool `json:"requiresTrust"` +} + +// 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: ModelProviderConfigurationPlan is part of an experimental API and may +// change or be removed. +type ModelProviderConfigurationPlan struct { + // Model definition prepared from the discovered model. Capability fields the provider did + // not report stay omitted rather than being asserted false. + Model ProviderModelConfig `json:"model"` + // Whether `model` still needs to be registered. When `alreadyConfigured`, `selectionId` is + // already registered and the caller can select it without adding anything. + ModelDisposition ModelProviderConfigurationDisposition `json:"modelDisposition"` + // Provider connection prepared from the instance's inference metadata. Carries no + // credential; supply one if the endpoint requires it. + Provider NamedProviderConfig `json:"provider"` + // 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. + ProviderDisposition ModelProviderConfigurationDisposition `json:"providerDisposition"` + // Provider-qualified selection id (`provider/id`) to pass to `switchTo` once the plan is + // applied. + SelectionID string `json:"selectionId"` + // Non-fatal warnings carried over from the discovered model, such as capabilities the + // provider did not report. + Warnings []ModelProviderWarning `json:"warnings"` +} + // 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. @@ -9711,6 +10731,126 @@ type ModelProviderDescriptor struct { Ordering int64 `json:"ordering"` } +// Provider discovery parameters. +// Experimental: ModelProviderDiscoverRequest is part of an experimental API and may change +// or be removed. +type ModelProviderDiscoverRequest struct { + // Opaque adapter identity returned by `session.providers.getCatalog`. + AdapterID string `json:"adapterId"` + // 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. + Input any `json:"input,omitempty"` +} + +// Provider instances found by a discovery operation. +// Experimental: ModelProviderDiscoverResult is part of an experimental API and may change +// or be removed. +type ModelProviderDiscoverResult struct { + // Discovered provider instances. Empty when passive default discovery finds no reachable + // provider. + Instances []ModelProviderInstance `json:"instances"` + // Typed operation outcome. Passive discovery can return `absent` with an empty instance + // list. + Outcome ModelProviderOperationOutcome `json:"outcome"` +} + +// Provider status request parameters. +// Experimental: ModelProviderGetStatusRequest is part of an experimental API and may change +// or be removed. +type ModelProviderGetStatusRequest struct { + // Provider instance reference returned by discovery. + Instance ModelProviderInstanceReference `json:"instance"` +} + +// A normalized model-provider instance discovered by the runtime. +// Experimental: ModelProviderInstance is part of an experimental API and may change or be +// removed. +type ModelProviderInstance struct { + // Human-readable instance name. + DisplayName string `json:"displayName"` + // Inference API endpoint when the provider exposes one separately from its management + // endpoint. + InferenceEndpoint *string `json:"inferenceEndpoint,omitempty"` + // Transport to use for inference against this instance. + InferenceTransport *ProviderEndpointTransport `json:"inferenceTransport,omitempty"` + // Provider family to use for inference against this instance. + InferenceType *ProviderEndpointType `json:"inferenceType,omitempty"` + // Wire API to use for inference against this instance, when required by the provider family. + InferenceWireAPI *ProviderEndpointWireAPI `json:"inferenceWireApi,omitempty"` + // Attribution for the adapter that produced this instance. + Provenance ModelProviderProvenance `json:"provenance"` + // Self-contained reference for subsequent provider operations. + Reference ModelProviderInstanceReference `json:"reference"` +} + +// Serializable reference to a discovered provider instance. +// Experimental: ModelProviderInstanceReference is part of an experimental API and may +// change or be removed. +type ModelProviderInstanceReference struct { + // Stable opaque identity of the adapter that owns this reference. Must be present in the + // target session's effective catalog. + AdapterID string `json:"adapterId"` + // Stable instance identifier derived by the provider adapter, such as + // `ollama:{normalizedEndpoint}`. + ID string `json:"id"` + // 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. + ManagementEndpoint string `json:"managementEndpoint"` + // Descriptive provider family. Must match the selected adapter; not a routing key. + ProviderKind string `json:"providerKind"` +} + +// Provider model inventory request parameters. +// Experimental: ModelProviderModelsListRequest is part of an experimental API and may +// change or be removed. +type ModelProviderModelsListRequest struct { + // Provider instance reference returned by discovery. + Instance ModelProviderInstanceReference `json:"instance"` +} + +// Typed provider-operation outcome. Use the code for control flow and the optional message +// for display. +// Experimental: ModelProviderOperationOutcome is part of an experimental API and may change +// or be removed. +type ModelProviderOperationOutcome struct { + // Machine-readable operation outcome. + Code ModelProviderOperationOutcomeCode `json:"code"` + // Human-readable detail for non-success outcomes. + Message *string `json:"message,omitempty"` +} + +// 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: ModelProviderPrepareConfigurationRequest is part of an experimental API and +// may change or be removed. +type ModelProviderPrepareConfigurationRequest struct { + // The discovered instance that serves the model. + Instance ModelProviderInstance `json:"instance"` + // The discovered model to configure. + Model DiscoveredModel `json:"model"` +} + +// Attribution for the adapter that produced a provider row. +// Experimental: ModelProviderProvenance is part of an experimental API and may change or be +// removed. +type ModelProviderProvenance struct { + // Stable opaque adapter identity from the effective catalog. Treat this as a whole + // identifier, not a parseable owner or kind. + AdapterID string `json:"adapterId"` + // Human-readable contributor name, not the adapter display name. + OwnerDisplayName *string `json:"ownerDisplayName,omitempty"` + // Stable contributor identifier when the adapter has an owner outside the runtime. + // Independent of the contribution mechanism and not a routing key. + OwnerID *string `json:"ownerId,omitempty"` + // Descriptive provider family that produced this row; not a routing key. + ProviderKind string `json:"providerKind"` + // Kind of component that supplied the adapter. + Source ModelProviderProvenanceSource `json:"source"` +} + // A neutral reference to the model provider that produced a model: an opaque id, a // human-readable label, and the provider kind. Carried on each enumerated Model so // consumers can group by provider without reaching into a provider-shaped internal type. @@ -9727,6 +10867,30 @@ type ModelProviderRef struct { Label string `json:"label"` } +// Current health information for a provider instance. +// Experimental: ModelProviderStatus is part of an experimental API and may change or be +// removed. +type ModelProviderStatus struct { + // Normalized provider instance. + Instance ModelProviderInstance `json:"instance"` + // Typed operation outcome. + Outcome ModelProviderOperationOutcome `json:"outcome"` + // Open provider status value, such as `healthy`, `unreachable`, or `notInstalled`. + Status string `json:"status"` + // Provider-reported version. + Version *string `json:"version,omitempty"` +} + +// A non-fatal provider observation warning. +// Experimental: ModelProviderWarning is part of an experimental API and may change or be +// removed. +type ModelProviderWarning struct { + // Machine-readable warning code. + Code string `json:"code"` + // Human-readable warning message. + Message string `json:"message"` +} + // 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 @@ -9855,12 +11019,17 @@ type ModelSwitchToRequest struct { ModelCapabilities *ModelCapabilitiesOverride `json:"modelCapabilities,omitempty"` // Settings scope used when persisting the selected model. ModelChangeScope *string `json:"modelChangeScope,omitempty"` - // Model selection id to switch to, as returned by `list`. A bare id (e.g. - // `claude-sonnet-4.6`) names a Copilot (CAPI) model; a provider-qualified id - // (`provider/id`, e.g. `acme/claude-sonnet`) targets a registry BYOK model. + // 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. ModelID string `json:"modelId"` // Optional settings context and explicit-override flags used to persist a picker selection. PickerPersistence *ModelPickerPersistenceRequest `json:"pickerPersistence,omitempty"` + // 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. + ProviderID *string `json:"providerId,omitempty"` // 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 @@ -9945,6 +11114,8 @@ type ModeSetRequest struct { PlanModel *string `json:"planModel,omitempty"` // Whether a dedicated plan model is configured. PlanModelConfigured *bool `json:"planModelConfigured,omitempty"` + // Provider owning planModel. Omit for legacy bare-model selection. + PlanModelProviderID *string `json:"planModelProviderId,omitempty"` // Reasoning effort to use with the dedicated plan model. PlanReasoningEffort *string `json:"planReasoningEffort,omitempty"` // Whether leaving plan mode should restore the session's previous model. @@ -10004,6 +11175,9 @@ type NamedProviderConfig struct { HasBearerTokenProvider *bool `json:"hasBearerTokenProvider,omitempty"` // Additional HTTP headers included with provider requests. Headers map[string]string `json:"headers,omitzero"` + // The product serving the provider's models, reported in telemetry as `model_provider`. + // Only affects telemetry. + ModelProvider *ProviderConfigModelProvider `json:"modelProvider,omitempty"` // Unique provider name used to qualify model selection IDs. Name string `json:"name"` // Transport used to communicate with the provider. @@ -10111,6 +11285,39 @@ type OptionsUpdateAdditionalContentExclusionPolicyRuleSource struct { Type string `json:"type"` } +// External tool call of the session or one of its sub-agents that is still waiting for +// session.tools.handlePendingToolCall. +// Experimental: PendingExternalToolRequest is part of an experimental API and may change or +// be removed. +type PendingExternalToolRequest struct { + // 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 + AgentID *string `json:"agentId,omitempty"` + // Arguments to pass to the external tool + Arguments any `json:"arguments,omitempty"` + // Stable identity of the provider that offered the tool, for hosts that route + // extension-owned tools by provider + ProviderID *string `json:"providerId,omitempty"` + // Request ID to pass to session.tools.handlePendingToolCall + RequestID string `json:"requestId"` + // Tool call ID assigned to this external tool invocation + ToolCallID string `json:"toolCallId"` + // Name of the external tool to invoke + ToolName string `json:"toolName"` +} + +// External tool calls still waiting for a result. +// Experimental: PendingExternalToolRequestList is part of an experimental API and may +// change or be removed. +type PendingExternalToolRequestList struct { + // 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. + Items []PendingExternalToolRequest `json:"items"` +} + // Pending permission prompt reconstructed from event history, with request ID and // user-facing prompt details. // Experimental: PendingPermissionRequest is part of an experimental API and may change or @@ -11965,6 +13172,10 @@ type ProviderConfig struct { // Well-known model ID used for capability lookup. When set, agent behavior config and token // limits are inferred from this model. ModelID *string `json:"modelId,omitempty"` + // 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. + ModelProvider *ProviderConfigModelProvider `json:"modelProvider,omitempty"` // Provider name used for model and telemetry attribution. ProviderName *string `json:"providerName,omitempty"` // Provider transport. Defaults to "http". @@ -12062,6 +13273,87 @@ type ProviderModelConfig struct { WireModel *string `json:"wireModel,omitempty"` } +// Authoritative budget measurements and policy metadata, independent of provider. +// Experimental: ProviderQuotaBudgetMetadata is part of an experimental API and may change +// or be removed. +type ProviderQuotaBudgetMetadata struct { + // Exact measured consumption in the budget's unit. + Consumed float64 `json:"consumed"` + // Exact budget entitlement, retaining incumbent fractional-unit compatibility. + Entitlement float64 `json:"entitlement"` + // Usage beyond entitlement, in the budget's unit. + Overage float64 `json:"overage"` + // Whether additional usage is allowed when the budget is exhausted. + OverageAllowedWhenExhausted bool `json:"overageAllowedWhenExhausted"` + // Optional additional-usage budget cap. + OverageLimit *float64 `json:"overageLimit,omitempty"` + // Service-reported remaining percentage for authoritative budget presentation. + RemainingPercentage float64 `json:"remainingPercentage"` + // Reset instant in epoch milliseconds when this budget actually defines a window. + ResetAtEpochMs *float64 `json:"resetAtEpochMs,omitempty"` + // Whether the reset instant is an estimate. + ResetEstimated *bool `json:"resetEstimated,omitempty"` + // Whether this budget uses token-based billing. + TokenBasedBilling *bool `json:"tokenBasedBilling,omitempty"` + // Whether the budget has unlimited entitlement. + Unlimited bool `json:"unlimited"` + // Whether service policy allows continued usage after exhaustion. + UsageAllowedWhenExhausted bool `json:"usageAllowedWhenExhausted"` +} + +// An account quota reading. Absence of a quantity is unknown, never zero. +// Experimental: ProviderQuotaState is part of an experimental API and may change or be +// removed. +type ProviderQuotaState struct { + // Whether the service permits access, independently of balance. + AccessState ProviderQuotaAccessState `json:"accessState"` + // Service error code or client acquisition category, separate from the business-state + // reason. + AcquisitionError *string `json:"acquisitionError,omitempty"` + // Acquisition outcome. Unavailable/failed readings have no service verdict; access/capacity + // unavailable are compatibility placeholders only. + AcquisitionStatus *ProviderQuotaAcquisitionStatus `json:"acquisitionStatus,omitempty"` + // Independently reported signed 64-bit available quantity. Zero does not override the + // service access/capacity verdict; omission and null are preserved. + AvailableQuantity *int64 `json:"availableQuantity,omitempty"` + // Optional metadata for an authoritative budget. Advisory balances and admission-only + // observations do not populate this. + BudgetMetadata *ProviderQuotaBudgetMetadata `json:"budgetMetadata,omitempty"` + // Service capacity state; missing quantities do not imply exhaustion. + CapacityState ProviderQuotaCapacityState `json:"capacityState"` + // Key for the backwards-compatible snapshots projection, when the authoritative budget + // supports that contract. + CompatibilityKey *string `json:"compatibilityKey,omitempty"` + // Independently reported signed 64-bit entitlement. -1 is an unlimited sentinel, not a + // capacity-state rewrite; omission and null are preserved. + EntitledQuantity *int64 `json:"entitledQuantity,omitempty"` + // Explicit service admission flag, when reported. + HasQuota *bool `json:"hasQuota,omitempty"` + // HTTP status from acquisition, when available. + HTTPStatus *int64 `json:"httpStatus,omitempty"` + // Whether this is a GET account reading or a pre-response admission observation. + // Observations are never merged across kinds. + ObservationKind *ProviderQuotaObservationKind `json:"observationKind,omitempty"` + // When the runtime observed this reading, not a charge timestamp or guarantee that + // consumption has settled. + ObservedAt *string `json:"observedAt,omitempty"` + // Provider/account whose service reported this state. + Provider ModelProviderRef `json:"provider"` + // Whether quantities are authoritative budget measurements, advisory balances, or absent. + // Only authoritative budgets support percentage presentation. + QuantityKind *ProviderQuotaQuantityKind `json:"quantityKind,omitempty"` + // Service-owned quota identifier within this provider. + QuotaID string `json:"quotaId"` + // Service-reported explanation for the state. + Reason *string `json:"reason,omitempty"` + // Service name owning this reading. + Service *string `json:"service,omitempty"` + // Service-reported quota source. + Source *string `json:"source,omitempty"` + // Unit of entitledQuantity and availableQuantity. + Unit ProviderQuotaUnit `json:"unit"` +} + // 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: ProviderSessionToken is part of an experimental API and may change or be @@ -12896,6 +14188,23 @@ type QueueWithdrawMessageResult struct { Removed bool `json:"removed"` } +// Pending warnings returned and cleared by session.quota.takeWarnings. +// Experimental: QuotaTakeWarningsResult is part of an experimental API and may change or be +// removed. +type QuotaTakeWarningsResult []QuotaWarningProjection + +// A pending session quota warning, consumed once by takeWarnings. +// Experimental: QuotaWarningProjection is part of an experimental API and may change or be +// removed. +type QuotaWarningProjection struct { + // User-facing warning text. + Message string `json:"message"` + // Related account or upgrade link. + URL *string `json:"url,omitempty"` + // Existing warning category. + WarningType string `json:"warningType"` +} + // Event type to register consumer interest for, used by runtime gating logic. // Experimental: RegisterEventInterestParams is part of an experimental API and may change // or be removed. @@ -13428,12 +14737,15 @@ type SandboxHostCapability struct { // 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, and a policy that uses it must also set `network.allowLocalNetwork`), - // `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). + // 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). Name string `json:"name"` // 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. @@ -13449,17 +14761,20 @@ type SandboxHostCapability struct { // Bubblewrap's private network namespace, such as slirp4netns. `network_filtering` — host // rules and the sandbox proxy (`network.allowedHosts`, `network.blockedHosts`, // `network.proxy`); on Linux this needs the same tooling as `network`; on Windows it needs -// a version with Process Security Environment 1.1 host-loopback support, and a policy that -// uses it must also set `network.allowLocalNetwork`, because Windows reaches the local -// proxy only together with private-network access. `denied_paths` — native enforcement of -// `filesystem.deniedPaths`; on Windows this needs a version whose sandbox contract reports -// denied-path support. `shell` — shell commands inside the sandbox: bash on macOS and -// Linux, PowerShell on Windows. `filesystem_enumeration` — enumerate-only filesystem -// grants, which PowerShell's drive roots use on Windows; this needs a version with Process -// Security Environment 1.1 filesystem enumeration support. Without it, sandboxed PowerShell -// still runs, but `Get-Location` may report the drive root, `Set-Location` may fail, and -// relative paths may resolve against the drive root; the session also receives a -// `session.warning` with `warningType` `sandbox`. Other platforms always report it. +// 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`, because Windows reaches the local proxy only together with +// private-network access. 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`; on Windows this needs a +// version whose sandbox contract reports denied-path support. `shell` — shell commands +// inside the sandbox: bash on macOS and Linux, PowerShell on Windows. +// `filesystem_enumeration` — enumerate-only filesystem grants, which PowerShell's drive +// roots use on Windows; this needs a version with Process Security Environment 1.1 +// filesystem enumeration support. Without it, sandboxed PowerShell still runs, but +// `Get-Location` may report the drive root, `Set-Location` may fail, and relative paths may +// resolve against the drive root; the session also receives a `session.warning` with +// `warningType` `sandbox`. Other platforms always report it. // Experimental: SandboxHostCapabilityName is part of an experimental API and may change or // be removed. type SandboxHostCapabilityName string @@ -13492,6 +14807,41 @@ type SandboxMaskedEnvVar struct { InjectHosts []string `json:"injectHosts"` } +// 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. +type SandboxProxyCaCreateResult struct { + // Absolute path of the public certificate of the certificate authority, in PEM format. + CertificatePath string `json:"certificatePath"` +} + +// 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: SandboxProxyCaRequest is part of an experimental API and may change or be +// removed. +type SandboxProxyCaRequest struct { + // 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. + SandboxConfig *SandboxConfig `json:"sandboxConfig,omitempty"` +} + +// Status of the persistent certificate authority of the sandbox credential proxy. +// Experimental: SandboxProxyCaStatus is part of an experimental API and may change or be +// removed. +type SandboxProxyCaStatus struct { + // 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. + CanInstall bool `json:"canInstall"` + // Human-readable reason for the state. On `installed` or `notInstalled`, present only when + // the certificate authority must be rotated, and then says why. + Detail *string `json:"detail,omitempty"` + // The state of the certificate authority. + State SandboxProxyCaState `json:"state"` +} + // Register an absolute-time scheduled prompt. // Experimental: ScheduleAddAtRequest is part of an experimental API and may change or be // removed. @@ -14018,6 +15368,16 @@ type SessionCompletionItem struct { RangeStart *int64 `json:"rangeStart,omitempty"` } +// The IDE a host is connected to, as reported to the session. +// Experimental: SessionConnectedIdeInfo is part of an experimental API and may change or be +// removed. +type SessionConnectedIdeInfo struct { + // Display name of the connected IDE, for example `VS Code`. + IdeName string `json:"ideName"` + // Absolute path of the workspace folder the IDE has open. + WorkspaceFolder string `json:"workspaceFolder"` +} + // Pre-resolved working-directory context for session startup. // Experimental: SessionContext is part of an experimental API and may change or be removed. type SessionContext struct { @@ -14038,7 +15398,8 @@ type SessionContext struct { // Experimental: SessionContextAttribution is part of an experimental API and may change or // be removed. type SessionContextAttribution struct { - // Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors + // Output reservation overlapping the displayed prompt allowance plus the tokens past the + // effective input budget's buffer-exhaustion blocking threshold. Mirrors // `SessionContextInfo.bufferTokens`. BufferTokens int64 `json:"bufferTokens"` // The six normalized `/context` header buckets, computed from the same tokenization as @@ -14054,9 +15415,9 @@ type SessionContextAttribution struct { // Flat list of per-source attribution entries. Group by `kind` and render unrecognized // kinds generically. Nesting and rollups are expressed via `parentId`. Entries []SessionContextAttributionEntriesItem `json:"entries"` - // Prompt limit plus the model's output reserve: the full context window - // `categories.freeSpace` and `categories.buffer` are measured against. Mirrors - // `SessionContextInfo.limit`. + // 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`. Limit int64 `json:"limit"` // 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 @@ -14067,8 +15428,8 @@ type SessionContextAttribution struct { // `autoResolved` (the model Auto resolved to), `selected` (the user's explicitly selected // model), `default` (a fallback before any model is known). ModelSource string `json:"modelSource"` - // Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` - // context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. + // Effective input budget after reserving requested output against the combined context + // ceiling. Mirrors `SessionContextInfo.promptTokenLimit`. PromptTokenLimit int64 `json:"promptTokenLimit"` // Total token count of the current context window the entries are measured against (system // message + conversation messages + tool definitions — the same total reported by @@ -14081,7 +15442,7 @@ type SessionContextAttribution struct { // describe window capacity rather than occupied context, so the values do not sum to // `totalTokens`. type SessionContextAttributionCategories struct { - // Output reserve plus post-blocking-threshold buffer. + // Overlapping output reservation plus post-blocking-threshold buffer. Buffer int64 `json:"buffer"` // Custom-instructions tokens (0 when none are configured). CustomInstructions int64 `json:"customInstructions"` @@ -14131,22 +15492,32 @@ type SessionContextAttributionEntriesItem struct { // Experimental: SessionContextInfo is part of an experimental API and may change or be // removed. type SessionContextInfo struct { - // Output reserve plus tokens after the buffer-exhaustion blocking threshold (default 95%) + // Output reservation overlapping the displayed prompt allowance plus tokens after the + // effective input budget's buffer-exhaustion blocking threshold (default 95%). BufferTokens int64 `json:"bufferTokens"` // Token count at which background compaction starts (configurable percentage of // promptTokenLimit) CompactionThreshold int64 `json:"compactionThreshold"` // Tokens consumed by user/assistant/tool messages ConversationTokens int64 `json:"conversationTokens"` - // Prompt token limit plus the model's full output token limit. + // Provider-owned model display label, or the Auto routing label. Presentation only; + // modelName remains the tokenization identity. + DisplayModelName *string `json:"displayModelName,omitempty"` + // Advertised prompt allowance for the selected context tier, without adding output tokens. + // The denominator for context-usage displays. Limit int64 `json:"limit"` // Tokens consumed by MCP tool definitions (subset of toolDefinitionsTokens, excludes // deferred tools) MCPToolsTokens int64 `json:"mcpToolsTokens"` // The model used for token counting ModelName string `json:"modelName"` - // Maximum prompt tokens allowed by the model (or DEFAULT_TOKEN_LIMIT if unspecified) + // 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. PromptTokenLimit int64 `json:"promptTokenLimit"` + // Provider whose catalog supplies this context window. Omitted when provider identity is + // unavailable. + Provider *ModelProviderRef `json:"provider,omitempty"` // Tokens consumed by the system prompt SystemTokens int64 `json:"systemTokens"` // Tokens consumed by tool definitions sent to the model (excludes deferred tools) @@ -14207,6 +15578,9 @@ type SessionFSError struct { Code SessionFSErrorCode `json:"code"` // Free-form detail about the error, for logging/diagnostics Message *string `json:"message,omitempty"` + // For failed writeFile requests only: true if the provider changed the target before + // failing. Omit when unknown or unchanged. + WriteChanged *bool `json:"writeChanged,omitempty"` } // Path to test for existence in the client-provided session filesystem. @@ -14295,6 +15669,26 @@ type SessionFSReaddirWithTypesResult struct { Error *SessionFSError `json:"error,omitempty"` } +// Path of the binary file to read from the client-provided session filesystem. +// Experimental: SessionFSReadFileBytesRequest is part of an experimental API and may change +// or be removed. +type SessionFSReadFileBytesRequest struct { + // Path using SessionFs conventions + Path string `json:"path"` + // Target session identifier + SessionID string `json:"sessionId"` +} + +// File bytes as standard base64, or a filesystem error if the read failed. +// Experimental: SessionFSReadFileBytesResult is part of an experimental API and may change +// or be removed. +type SessionFSReadFileBytesResult struct { + // Exact file bytes encoded as standard base64 + Content string `json:"content"` + // Describes a filesystem error. + Error *SessionFSError `json:"error,omitempty"` +} + // Path of the file to read from the client-provided session filesystem. // Experimental: SessionFSReadFileRequest is part of an experimental API and may change or // be removed. @@ -14347,6 +15741,9 @@ type SessionFSRmRequest struct { // Experimental: SessionFSSetProviderCapabilities is part of an experimental API and may // change or be removed. type SessionFSSetProviderCapabilities struct { + // Whether the provider supports binary reads and writes through sessionFs.readFileBytes and + // sessionFs.writeFileBytes + Binary *bool `json:"binary,omitempty"` // Whether the provider supports SQLite query/exists operations Sqlite *bool `json:"sqlite,omitempty"` } @@ -14501,6 +15898,21 @@ type SessionFSStatResult struct { Size int64 `json:"size"` } +// File path, standard-base64-encoded bytes to write, and optional mode for the +// client-provided session filesystem. +// Experimental: SessionFSWriteFileBytesRequest is part of an experimental API and may +// change or be removed. +type SessionFSWriteFileBytesRequest struct { + // Exact file bytes encoded as standard base64 + Content string `json:"content"` + // Optional POSIX-style mode for newly created files + Mode *int64 `json:"mode,omitempty"` + // Path using SessionFs conventions + Path string `json:"path"` + // Target session identifier + SessionID string `json:"sessionId"` +} + // File path, content to write, and optional mode for the client-provided session filesystem. // Experimental: SessionFSWriteFileRequest is part of an experimental API and may change or // be removed. @@ -14641,11 +16053,6 @@ type SessionInstalledPluginSourceURL struct { URL string `json:"url"` } -// Experimental: SessionInstructionsReloadResult is part of an experimental API and may -// change or be removed. -type SessionInstructionsReloadResult struct { -} - // Baseline data provenance for a prediction. // Experimental: SessionLimitPredictionBaselineData is part of an experimental API and may // change or be removed. @@ -14863,12 +16270,21 @@ type SessionManagedPermissions struct { Ask []string `json:"ask,omitzero"` // Permission rules that block matching operations. Deny has highest precedence. Deny []string `json:"deny,omitzero"` + // When true, prevents Assisted Permissions from being activated. An actively Assisted + // session falls back to Manual Approval while the policy is in force. Omit the key or set + // it to false to impose no restriction. + DisableAssistedPermissionsMode *bool `json:"disableAssistedPermissionsMode,omitempty"` // When set to `disable`, prevents bypass/allow-all permission modes. Advisory auto-approval // remains available because normal prompt paths stay active. Any other value is accepted // rather than failing the session, but is enforced as `disable`: the key is only present to // restrict something, so a mode this runtime cannot interpret fails closed to the most // restrictive one it knows. Omit the key entirely to impose no restriction. DisableBypassPermissionsMode *string `json:"disableBypassPermissionsMode,omitempty"` + // Closed-world host boundary expressed as `Domain(hostname)`, `Domain(IP)`, or + // `Domain(*.example.com)` rules. Schemes, ports, paths, queries, and fragments are rejected + // because every network request must be enforceable at host-level egress. Multiple managed + // sources intersect their lists; an empty list denies all hosts. + LimitTo []string `json:"limitTo,omitzero"` } // Managed settings an SDK host may inject at session startup. Only permissions are accepted @@ -14924,6 +16340,11 @@ type SessionMCPOauthCancelLoginResult struct { Cancelled bool `json:"cancelled"` } +// Experimental: SessionMCPOauthCompleteResult is part of an experimental API and may change +// or be removed. +type SessionMCPOauthCompleteResult struct { +} + // Effect-free preparation bound to the existing local session, requester and installation, // with frozen options. // Experimental: SessionMCPOauthPrepareLoginRequest is part of an experimental API and may @@ -14967,6 +16388,20 @@ type SessionMCPReloadResult struct { type SessionMCPRestartServerResult struct { } +// Records which IDE the host is connected to, or clears it. +// Experimental: SessionMCPSetConnectedIdeInfoParams is part of an experimental API and may +// change or be removed. +type SessionMCPSetConnectedIdeInfoParams struct { + // The connected IDE. Null or omitted clears the recorded IDE, which is how a host reports + // that it is disconnected. + Ide *SessionConnectedIdeInfo `json:"ide,omitempty"` +} + +// Experimental: SessionMCPSetConnectedIdeInfoResult is part of an experimental API and may +// change or be removed. +type SessionMCPSetConnectedIdeInfoResult struct { +} + // Experimental: SessionMCPStartServerResult is part of an experimental API and may change // or be removed. type SessionMCPStartServerResult struct { @@ -15032,10 +16467,30 @@ type SessionMetadataSnapshot struct { WorkspacePath *string `json:"workspacePath"` } +// Experimental: SessionModelClearStartupSeedRequest is part of an experimental API and may +// change or be removed. +// Internal: SessionModelClearStartupSeedRequest is an internal SDK API and is not part of +// the public surface. +type SessionModelClearStartupSeedRequest struct { + ExpectedModel string `json:"expectedModel"` + // Provider captured with the startup seed; omission matches only an unqualified seed. + ExpectedProviderID *string `json:"expectedProviderId,omitempty"` +} + +// Experimental: SessionModelClearStartupSeedResult is part of an experimental API and may +// change or be removed. +// Internal: SessionModelClearStartupSeedResult is an internal SDK API and is not part of +// the public surface. +type SessionModelClearStartupSeedResult struct { + Cleared bool `json:"cleared"` +} + // The list of models available to this session. // Experimental: SessionModelList is part of an experimental API and may change or be // removed. type SessionModelList struct { + // Ordered Auto routing preferences discovered for this session's account. + Auto *AutoTierMetadata `json:"auto,omitempty"` // 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`). @@ -15106,6 +16561,12 @@ type SessionOpenOptions struct { AuthClientIDMetadataURL *string `json:"authClientIdMetadataUrl,omitempty"` // Initial authentication info for the session. AuthInfo AuthInfo `json:"authInfo,omitempty"` + // Whether a CLI host explicitly requested the initial Auto preference. False preserves a + // settings-derived preference without validating availability during creation; execution + // still validates it. Defaults to true and is ignored for non-CLI callers. + // Internal: AutoTierIsExplicit is part of the SDK's internal API surface and is not + // intended for external use. + AutoTierIsExplicit *bool `json:"autoTierIsExplicit,omitempty"` // Allowlist of available tool names. AvailableTools []string `json:"availableTools,omitzero"` // Options scoped to the built-in CAPI (Copilot API) provider. @@ -15173,6 +16634,10 @@ type SessionOpenOptions struct { EnableSkills *bool `json:"enableSkills,omitempty"` // Whether model responses stream as delta events. EnableStreaming *bool `json:"enableStreaming,omitempty"` + // Opt in to enforcing non-overridable managed model controls on session model, Auto-tier, + // reasoning-effort, and context-tier changes. Managed defaults still apply when omitted; + // this option only turns conflicting changes into errors. + EnforceManagedModelDefaults *bool `json:"enforceManagedModelDefaults,omitempty"` // How MCP server environment values are interpreted. EnvValueMode *SessionOpenOptionsEnvValueMode `json:"envValueMode,omitempty"` // Override directory for session event logs. @@ -15614,6 +17079,23 @@ type SessionPluginsReloadRequest struct { type SessionPluginsReloadResult struct { } +// Experimental: SessionPluginsRetryManagedRequest is part of an experimental API and may +// change or be removed. +type SessionPluginsRetryManagedRequest struct { + // "plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed + // settings require. + Plugins []string `json:"plugins,omitzero"` +} + +// Per-plugin outcomes of retrying the plugins required by the session's managed settings. +// Experimental: SessionPluginsRetryManagedResult is part of an experimental API and may +// change or be removed. +type SessionPluginsRetryManagedResult struct { + // One entry per requested plugin, in request order. When no plugins were requested, one + // entry per plugin the managed settings require. + Plugins []ManagedPluginRetryEntry `json:"plugins"` +} + // Experimental: SessionPluginsUninstallResult is part of an experimental API and may change // or be removed. type SessionPluginsUninstallResult struct { @@ -15664,6 +17146,147 @@ type SessionQueueProcessResult struct { type SessionQueueSetDrainPausedResult struct { } +// Existing delegation guidance for an account without delegation access. +// Experimental: SessionQuotaDelegateWarning is part of an experimental API and may change +// or be removed. +type SessionQuotaDelegateWarning struct { + // User-facing guidance. + Text string `json:"text"` + // Link to upgrade the account. + URL string `json:"url"` +} + +// The incumbent session-owned quota and account projection. +// Experimental: SessionQuotaGetResult is part of an experimental API and may change or be +// removed. +type SessionQuotaGetResult struct { + // Whether the account can sign up for Copilot Free. + CanSignupForCopilotFree bool `json:"canSignupForCopilotFree"` + // Whether the account can delegate tasks to GitHub. + DelegateAvailable bool `json:"delegateAvailable"` + // Existing delegation warning for a free account. + DelegateWarning *SessionQuotaDelegateWarning `json:"delegateWarning,omitempty"` + // Whether dynamic workflows are enabled for the active account. + DynamicWorkflowsEnabled bool `json:"dynamicWorkflowsEnabled"` + // Whether dynamic workflows are visible under the session's feature flags. + DynamicWorkflowsUIVisible bool `json:"dynamicWorkflowsUiVisible"` + // Whether the account uses the free limited Copilot plan. + IsFreeUser bool `json:"isFreeUser"` + // Whether the account uses token-based billing. + IsTbbUser bool `json:"isTbbUser"` + // Whether model cost columns should be shown. + ModelCostColumnVisible bool `json:"modelCostColumnVisible"` + // Resolved Copilot plan tier. + PlanTier SessionQuotaPlanTier `json:"planTier"` + // Whether premium-request costs are billable. + PremiumRequestsBillable bool `json:"premiumRequestsBillable"` + // Provider-owned account quota state, including snapshots without numeric balances. + ProviderQuotas []ProviderQuotaState `json:"providerQuotas,omitzero"` + // Known quota snapshots, keyed by the provider's quota type. + Snapshots map[string]SessionQuotaSnapshot `json:"snapshots"` + // Upgrade link for a free account. + UpgradeURL *string `json:"upgradeUrl,omitempty"` +} + +// The incumbent session-owned quota and account projection. +// Experimental: SessionQuotaProjection is part of an experimental API and may change or be +// removed. +type SessionQuotaProjection struct { + // Whether the account can sign up for Copilot Free. + CanSignupForCopilotFree bool `json:"canSignupForCopilotFree"` + // Whether the account can delegate tasks to GitHub. + DelegateAvailable bool `json:"delegateAvailable"` + // Existing delegation warning for a free account. + DelegateWarning *SessionQuotaDelegateWarning `json:"delegateWarning,omitempty"` + // Whether dynamic workflows are enabled for the active account. + DynamicWorkflowsEnabled bool `json:"dynamicWorkflowsEnabled"` + // Whether dynamic workflows are visible under the session's feature flags. + DynamicWorkflowsUIVisible bool `json:"dynamicWorkflowsUiVisible"` + // Whether the account uses the free limited Copilot plan. + IsFreeUser bool `json:"isFreeUser"` + // Whether the account uses token-based billing. + IsTbbUser bool `json:"isTbbUser"` + // Whether model cost columns should be shown. + ModelCostColumnVisible bool `json:"modelCostColumnVisible"` + // Resolved Copilot plan tier. + PlanTier SessionQuotaPlanTier `json:"planTier"` + // Whether premium-request costs are billable. + PremiumRequestsBillable bool `json:"premiumRequestsBillable"` + // Provider-owned account quota state, including snapshots without numeric balances. + ProviderQuotas []ProviderQuotaState `json:"providerQuotas,omitzero"` + // Known quota snapshots, keyed by the provider's quota type. + Snapshots map[string]SessionQuotaSnapshot `json:"snapshots"` + // Upgrade link for a free account. + UpgradeURL *string `json:"upgradeUrl,omitempty"` +} + +// The incumbent session-owned quota and account projection. +// Experimental: SessionQuotaRefreshResult is part of an experimental API and may change or +// be removed. +type SessionQuotaRefreshResult struct { + // Whether the account can sign up for Copilot Free. + CanSignupForCopilotFree bool `json:"canSignupForCopilotFree"` + // Whether the account can delegate tasks to GitHub. + DelegateAvailable bool `json:"delegateAvailable"` + // Existing delegation warning for a free account. + DelegateWarning *SessionQuotaDelegateWarning `json:"delegateWarning,omitempty"` + // Whether dynamic workflows are enabled for the active account. + DynamicWorkflowsEnabled bool `json:"dynamicWorkflowsEnabled"` + // Whether dynamic workflows are visible under the session's feature flags. + DynamicWorkflowsUIVisible bool `json:"dynamicWorkflowsUiVisible"` + // Whether the account uses the free limited Copilot plan. + IsFreeUser bool `json:"isFreeUser"` + // Whether the account uses token-based billing. + IsTbbUser bool `json:"isTbbUser"` + // Whether model cost columns should be shown. + ModelCostColumnVisible bool `json:"modelCostColumnVisible"` + // Resolved Copilot plan tier. + PlanTier SessionQuotaPlanTier `json:"planTier"` + // Whether premium-request costs are billable. + PremiumRequestsBillable bool `json:"premiumRequestsBillable"` + // Provider-owned account quota state, including snapshots without numeric balances. + ProviderQuotas []ProviderQuotaState `json:"providerQuotas,omitzero"` + // Known quota snapshots, keyed by the provider's quota type. + Snapshots map[string]SessionQuotaSnapshot `json:"snapshots"` + // Upgrade link for a free account. + UpgradeURL *string `json:"upgradeUrl,omitempty"` +} + +// Session quota snapshot, preserving the incumbent numeric units and reset metadata. +// Experimental: SessionQuotaSnapshot is part of an experimental API and may change or be +// removed. +type SessionQuotaSnapshot struct { + // Entitled quantity in this quota's units. + EntitlementRequests float64 `json:"entitlementRequests"` + // Whether the provider reports available quota. + HasQuota *bool `json:"hasQuota,omitempty"` + // Whether the entitlement is unlimited. + IsUnlimitedEntitlement bool `json:"isUnlimitedEntitlement"` + // Additional usage in this quota's units. + Overage float64 `json:"overage"` + // Whether additional usage is allowed after quota exhaustion. + OverageAllowedWithExhaustedQuota bool `json:"overageAllowedWithExhaustedQuota"` + // Additional-usage budget cap, when provided. + OverageEntitlement *float64 `json:"overageEntitlement,omitempty"` + // Percentage of the entitlement remaining. + RemainingPercentage float64 `json:"remainingPercentage"` + // Quota reset time in milliseconds since the Unix epoch, when known. + ResetDateEpochMs *float64 `json:"resetDateEpochMs,omitempty"` + // Whether the reset time is estimated. + ResetDateEstimated *bool `json:"resetDateEstimated,omitempty"` + // Whether this quota uses token-based billing. + TokenBasedBilling *bool `json:"tokenBasedBilling,omitempty"` + // Whether usage is allowed after quota exhaustion. + UsageAllowedWithExhaustedQuota bool `json:"usageAllowedWithExhaustedQuota"` + // Consumed quantity in this quota's units. + UsedRequests float64 `json:"usedRequests"` +} + +// Pending warnings returned and cleared by session.quota.takeWarnings. +// Experimental: SessionQuotaTakeWarningsResult is part of an experimental API and may +// change or be removed. +type SessionQuotaTakeWarningsResult []QuotaWarningProjection + // Experimental: SessionRemoteDisableResult is part of an experimental API and may change or // be removed. type SessionRemoteDisableResult struct { @@ -15795,6 +17418,35 @@ type SessionsCloseResult struct { type SessionsConfigureSessionExtensionsResult struct { } +// Identity, state location and starting context for a workspace record. +// Experimental: SessionsCreateWorkspaceRequest is part of an experimental API and may +// change or be removed. +type SessionsCreateWorkspaceRequest struct { + // 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`. + Context *SessionWorkingDirectoryContextWithClient `json:"context,omitempty"` + // `windows` (any letter case) selects Windows path rules. Any other value selects POSIX + // path rules. + Convention string `json:"convention"` + // User-supplied display name for the workspace + Name *string `json:"name,omitempty"` + // Session ID the workspace record belongs to + SessionID string `json:"sessionId"` + // 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. + SessionStatePath string `json:"sessionStatePath"` +} + +// The workspace record that was written. +// Experimental: SessionsCreateWorkspaceResult is part of an experimental API and may change +// or be removed. +type SessionsCreateWorkspaceResult struct { + // The created workspace record, as JSON + WorkspaceJSON string `json:"workspaceJson"` +} + // Session ID to delete from disk. // Experimental: SessionsDeleteRequest is part of an experimental API and may change or be // removed. @@ -16231,6 +17883,25 @@ type SessionsLoadDeferredRepoHooksRequest struct { SessionID string `json:"sessionId"` } +// Where the session's state lives, as a root directory and the session ID under it. +// Experimental: SessionsLoadWorkspaceRequest is part of an experimental API and may change +// or be removed. +type SessionsLoadWorkspaceRequest struct { + // 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. + SessionID string `json:"sessionId"` + // Root directory every session's state directory sits under + SessionsHome string `json:"sessionsHome"` +} + +// The workspace record on disk, omitted when the session has none. +// Experimental: SessionsLoadWorkspaceResult is part of an experimental API and may change +// or be removed. +type SessionsLoadWorkspaceResult struct { + // The workspace record, as JSON. Omitted when the record does not exist. + WorkspaceJSON *string `json:"workspaceJson,omitempty"` +} + // `sessions.open` handoff progress update with step, status, and optional message. // Experimental: SessionsOpenProgress is part of an experimental API and may change or be // removed. @@ -16386,6 +18057,30 @@ type SessionsTransferRemoteControlRequest struct { ToSessionID string `json:"toSessionId"` } +// 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: SessionsUpdateWorkspaceFieldsRequest is part of an experimental API and may +// change or be removed. +type SessionsUpdateWorkspaceFieldsRequest struct { + // 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. + FieldsJSON string `json:"fieldsJson"` + // 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. + SessionID string `json:"sessionId"` + // Root directory every session's state directory sits under + SessionsHome string `json:"sessionsHome"` +} + +// The merge completed. The record carries the supplied workspace-schema fields, but a +// stored `fork_count` stays. +// Experimental: SessionsUpdateWorkspaceFieldsResult is part of an experimental API and may +// change or be removed. +type SessionsUpdateWorkspaceFieldsResult struct { +} + // Experimental: SessionSuspendResult is part of an experimental API and may change or be // removed. type SessionSuspendResult struct { @@ -16579,6 +18274,25 @@ type SessionUpdateOptionsResult struct { Success bool `json:"success"` } +// Internal absolute code-change totals reported by the owning host. +// Experimental: SessionUsageSetCodeChangesRequest is part of an experimental API and may +// change or be removed. +// Internal: SessionUsageSetCodeChangesRequest is an internal SDK API and is not part of the +// public surface. +type SessionUsageSetCodeChangesRequest struct { + // Absolute changed-file count; omission preserves the previous count. + FilesCount *float64 `json:"filesCount,omitempty"` + // Absolute added-line total, replacing the previous reading. + LinesAdded float64 `json:"linesAdded"` + // Absolute removed-line total, replacing the previous reading. + LinesRemoved float64 `json:"linesRemoved"` +} + +// Experimental: SessionUsageSetCodeChangesResult is part of an experimental API and may +// change or be removed. +type SessionUsageSetCodeChangesResult struct { +} + // Experimental: SessionWorkflowPauseAtCheckpointResult is part of an experimental API and // may change or be removed. type SessionWorkflowPauseAtCheckpointResult struct { @@ -16610,6 +18324,30 @@ type SessionWorkingDirectoryContext struct { RepositoryHost *string `json:"repositoryHost,omitempty"` } +// A working-directory context together with the client that produced it. +// Experimental: SessionWorkingDirectoryContextWithClient is part of an experimental API and +// may change or be removed. +type SessionWorkingDirectoryContextWithClient struct { + // Merge-base commit SHA + BaseCommit *string `json:"baseCommit,omitempty"` + // Current git branch name + Branch *string `json:"branch,omitempty"` + // Name of the client that created the session + ClientName *string `json:"clientName,omitempty"` + // Current working directory path + Cwd string `json:"cwd"` + // Root directory of the git repository + GitRoot *string `json:"gitRoot,omitempty"` + // Head commit of the current git branch + HeadCommit *string `json:"headCommit,omitempty"` + // Hosting platform type of the repository + HostType *string `json:"hostType,omitempty"` + // Repository identifier derived from the git remote URL + Repository *string `json:"repository,omitempty"` + // Raw host string from the git remote URL + RepositoryHost *string `json:"repositoryHost,omitempty"` +} + // Experimental: SessionWorkspacesCreateDirectoryResult is part of an experimental API and // may change or be removed. type SessionWorkspacesCreateDirectoryResult struct { @@ -16648,6 +18386,10 @@ func (RawSettableAuthInfoData) settableAuthInfo() {} func (r RawSettableAuthInfoData) settableAuthInfoType() SettableAuthInfoType { return r.Discriminator } +func (AccountAuthInfo) settableAuthInfo() {} +func (AccountAuthInfo) settableAuthInfoType() SettableAuthInfoType { + return SettableAuthInfoTypeAccount +} func (APIKeyAuthInfo) settableAuthInfo() {} func (APIKeyAuthInfo) settableAuthInfoType() SettableAuthInfoType { return SettableAuthInfoTypeAPIKey @@ -16749,7 +18491,8 @@ type ShellCredentials struct { Git *bool `json:"git,omitempty"` } -// Shell command to run, with optional working directory and timeout in milliseconds. +// Shell command to run, with optional working directory and timeout in milliseconds. Spawn +// failures return an RPC error. // Experimental: ShellExecRequest is part of an experimental API and may change or be // removed. type ShellExecRequest struct { @@ -16761,11 +18504,11 @@ type ShellExecRequest struct { Timeout *int64 `json:"timeout,omitempty"` } -// Identifier of the spawned process, used to correlate streamed output and exit -// notifications. +// Identifier of the spawned shell process, usable with shell.kill while the process is +// running. // Experimental: ShellExecResult is part of an experimental API and may change or be removed. type ShellExecResult struct { - // Unique identifier for tracking streamed output + // Identifier usable with shell.kill while the process is running ProcessID string `json:"processId"` } @@ -17533,8 +19276,8 @@ type SkillPlanUninstallRequest struct { PolicySessionID string `json:"policySessionId"` } -// Catalog-only metadata for one SDK-provided skill. The complete SKILL.md is fetched -// separately and lazily. +// Authoritative catalog metadata for one SDK-provided skill. The skill's SKILL.md text is +// fetched separately and lazily. // Experimental: SkillProviderDescriptor is part of an experimental API and may change or be // removed. type SkillProviderDescriptor struct { @@ -17582,15 +19325,18 @@ type SkillProviderReadRequest struct { SessionID string `json:"sessionId"` } -// Complete text-only SKILL.md content returned by an SDK session's skill provider. Related -// files and assets are not supported. +// Text-only SKILL.md content returned by an SDK session's skill provider. YAML frontmatter +// is optional: fields it omits come from the catalog descriptor, fields it declares must +// match the descriptor, and `allowed-tools` is read only from frontmatter. Related files +// and assets are not supported. // Experimental: SkillProviderReadResult is part of an experimental API and may change or be // removed. // Internal: SkillProviderReadResult is an internal SDK API and is not part of the public // surface. type SkillProviderReadResult struct { - // Complete SKILL.md text. The runtime enforces a 1 MiB UTF-8 byte limit. - Markdown string `json:"markdown"` + // SKILL.md text, with or without YAML frontmatter, or null when the provider has no skill + // with the requested name. The runtime enforces a 1 MiB UTF-8 byte limit. + Markdown *string `json:"markdown"` } // Skill names to mark as disabled in global configuration, replacing any previous list. @@ -18810,6 +20556,13 @@ type ToolsGetCurrentMetadataResult struct { type ToolsInitializeAndValidateResult struct { } +// No parameters; returns the external tool calls of the session and its sub-agents that are +// still waiting for a result. +// Experimental: ToolsListPendingRequestsRequest is part of an experimental API and may +// change or be removed. +type ToolsListPendingRequestsRequest struct { +} + // Optional model identifier whose tool overrides should be applied to the listing. // Experimental: ToolsListRequest is part of an experimental API and may change or be // removed. @@ -19360,6 +21113,9 @@ type UsageGetMetricsResult struct { // Per-agent usage metrics, keyed by agent instance identifier. The main conversation uses // the stable key `main`. AgentMetrics map[string]UsageMetricsAgentMetric `json:"agentMetrics,omitzero"` + // Completeness of totalNanoAiu. Numeric totals remain the reported subtotal; zero is not a + // claim of free usage when unavailable. + AiCreditsStatus *AiCreditsStatus `json:"aiCreditsStatus,omitempty"` // Aggregated code change metrics CodeChanges UsageMetricsCodeChanges `json:"codeChanges"` // Currently active model identifier @@ -19370,6 +21126,9 @@ type UsageGetMetricsResult struct { LastCallOutputTokens int64 `json:"lastCallOutputTokens"` // Per-model token and request metrics, keyed by model identifier ModelMetrics map[string]UsageMetricsModelMetric `json:"modelMetrics"` + // Provider-attributed model metrics, ordered by first observed provider and model. + // Null attribution preserves historical usage whose provider or model was not recorded. + ProviderModelMetrics []UsageMetricsProviderModelMetric `json:"providerModelMetrics,omitzero"` // ISO 8601 timestamp when the session started SessionStartTime time.Time `json:"sessionStartTime"` // Session-wide per-token-type accumulated token counts @@ -19424,6 +21183,9 @@ type UsageMetricsCodeChanges struct { // Experimental: UsageMetricsModelMetric is part of an experimental API and may change or be // removed. type UsageMetricsModelMetric struct { + // Completeness of this model's reported AI-credit subtotal. Explicitly reported zero is + // complete, not unavailable. + AiCreditsStatus *AiCreditsStatus `json:"aiCreditsStatus,omitempty"` // Latest known prompt-cache expiration for this model. A timestamp in the past indicates // that the observed cache has expired. CacheExpiresAt *time.Time `json:"cacheExpiresAt,omitempty"` @@ -19471,6 +21233,22 @@ type UsageMetricsModelMetricUsage struct { ReasoningTokens *int64 `json:"reasoningTokens,omitempty"` } +// Usage for one recorded provider and model, without merging identical model IDs across +// providers. +// Experimental: UsageMetricsProviderModelMetric is part of an experimental API and may +// change or be removed. +type UsageMetricsProviderModelMetric struct { + // Request, token, and cost totals for this provider/model. + Metrics UsageMetricsModelMetric `json:"metrics"` + // Model display name captured at call time, when known. + ModelDisplayName *string `json:"modelDisplayName,omitempty"` + // Model identity, or null for legacy aggregate-only usage. + ModelID *string `json:"modelId"` + // Provider identity and product label captured when the call was dispatched; null when + // unknown. + Provider *ModelProviderRef `json:"provider"` +} + // Session-wide token-detail entry containing the accumulated token count for one token type. // Experimental: UsageMetricsTokenDetail is part of an experimental API and may change or be // removed. @@ -19479,6 +21257,20 @@ type UsageMetricsTokenDetail struct { TokenCount int64 `json:"tokenCount"` } +// Internal absolute code-change totals reported by the owning host. +// Experimental: UsageSetCodeChangesRequest is part of an experimental API and may change or +// be removed. +// Internal: UsageSetCodeChangesRequest is an internal SDK API and is not part of the public +// surface. +type UsageSetCodeChangesRequest struct { + // Absolute changed-file count; omission preserves the previous count. + FilesCount *float64 `json:"filesCount,omitempty"` + // Absolute added-line total, replacing the previous reading. + LinesAdded float64 `json:"linesAdded"` + // Absolute removed-line total, replacing the previous reading. + LinesRemoved float64 `json:"linesRemoved"` +} + // Result of a user-requested shell command. // Experimental: UserRequestedShellCommandResult is part of an experimental API and may // change or be removed. @@ -19511,9 +21303,8 @@ type UserSettingMetadata struct { Value any `json:"value"` } -// Per-key metadata for every known user setting (settings.json overlaid with the legacy -// config.json, config.json wins), including settings left at their default. Excludes -// repository- and enterprise-managed overrides. +// 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 // removed. type UserSettingsGetResult struct { @@ -19522,11 +21313,6 @@ type UserSettingsGetResult struct { Settings map[string]UserSettingMetadata `json:"settings"` } -// Experimental: UserSettingsReloadResult is part of an experimental API and may change or -// be removed. -type UserSettingsReloadResult struct { -} - // 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 @@ -19536,14 +21322,9 @@ type UserSettingsSetRequest struct { Settings any `json:"settings"` } -// Outcome of writing user settings. // Experimental: UserSettingsSetResult is part of an experimental API and may change or be // removed. type UserSettingsSetResult struct { - // Top-level keys whose write landed in settings.json but is shadowed by a value still - // present in the legacy config.json (config.json wins on read). The write does not take - // effect until the legacy value is removed. - ShadowedKeys []string `json:"shadowedKeys"` } // The approval to add as a session-scoped rule @@ -20476,12 +22257,29 @@ type WorkflowToolRunRequest struct { ToolCallID *string `json:"toolCallId,omitempty"` } +// 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: WorkspaceDiffContents is part of an experimental API and may change or be +// removed. +type WorkspaceDiffContents struct { + // Complete current text read when computing the diff. Omitted for a deleted file; an empty + // string represents an existing empty file. + After *string `json:"after,omitempty"` + // Complete text before the session first changed the file. Omitted when the file did not + // exist; an empty string represents an existing empty file. + Before *string `json:"before,omitempty"` +} + // A single changed file and its unified diff. // Experimental: WorkspaceDiffFileChange is part of an experimental API and may change or be // removed. type WorkspaceDiffFileChange struct { // Type of change represented by this file diff. ChangeType WorkspaceDiffFileChangeType `json:"changeType"` + // 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. + Contents *WorkspaceDiffContents `json:"contents,omitempty"` // Unified diff content for the file. Empty when the diff was truncated. Diff string `json:"diff"` // Whether the diff content was omitted because it exceeded the per-file size limit. @@ -20594,6 +22392,11 @@ type WorkspacesDeleteAutopilotObjectiveResult struct { type WorkspacesDiffRequest struct { // When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. IgnoreWhitespace *bool `json:"ignoreWhitespace,omitempty"` + // 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. + IncludeContents *bool `json:"includeContents,omitempty"` // Diff mode requested by the client. Mode WorkspaceDiffMode `json:"mode"` } @@ -21077,6 +22880,21 @@ const ( AgentRegistrySpawnValidationErrorReasonYoloNotAllowed AgentRegistrySpawnValidationErrorReason = "yolo-not-allowed" ) +// Whether the accumulated numeric AI-credit subtotal covers the observed calls. +// Experimental: AiCreditsStatus is part of an experimental API and may change or be removed. +type AiCreditsStatus string + +const ( + // Every observed call supplied an AI-credit amount, including an explicit zero. + AiCreditsStatusComplete AiCreditsStatus = "complete" + // Some calls supplied amounts and some did not; the numeric value is only a reported + // subtotal. + AiCreditsStatusPartial AiCreditsStatus = "partial" + // No AI-credit amount was reported for the observed calls. Numeric zero is not a zero-cost + // claim. + AiCreditsStatusUnavailable AiCreditsStatus = "unavailable" +) + // Type of GitHub reference // Experimental: AttachmentGitHubReferenceType is part of an experimental API and may change // or be removed. @@ -21137,6 +22955,7 @@ const ( type AuthInfoType string const ( + AuthInfoTypeAccount AuthInfoType = "account" AuthInfoTypeAPIKey AuthInfoType = "api-key" AuthInfoTypeCopilotAPIToken AuthInfoType = "copilot-api-token" AuthInfoTypeEnv AuthInfoType = "env" @@ -21147,16 +22966,19 @@ const ( AuthInfoTypeUser AuthInfoType = "user" ) -// Terminal disposition of a login persistence attempt. +// Disposition of a login attempt, including pending user decisions. // Experimental: AuthLoginResultStatus is part of an experimental API and may change or be // removed. type AuthLoginResultStatus string const ( - // The credential was persisted and the account is signed in. + // The credential was persisted and the selected account is signed in. AuthLoginResultStatusCompleted AuthLoginResultStatus = "completed" // The user declined plaintext persistence. AuthLoginResultStatusDeclined AuthLoginResultStatus = "declined" + // Credentials are saved; select an account using a returned selectionId as advance input to + // complete sign-in. + AuthLoginResultStatusNeedsAccountSelection AuthLoginResultStatus = "needs-account-selection" // Persistence needs explicit consent to store the token in plaintext. AuthLoginResultStatusNeedsPlaintextConsent AuthLoginResultStatus = "needs-plaintext-consent" ) @@ -21221,8 +23043,8 @@ const ( AutopilotObjectiveStatusPaused AutopilotObjectiveStatus = "paused" ) -// Routing preference used when the session model is `auto`. `fast` is an integrator-only -// latency preset and is not a first-party GitHub Copilot product preference. +// Extensible routing preference for the virtual `auto` model. New identifiers must be +// advertised and enabled by the provider. `fast` is an integrator-only latency preset. // Experimental: AutoTier is part of an experimental API and may change or be removed. type AutoTier string @@ -21969,6 +23791,20 @@ const ( ConnectorConnectResultKindPending ConnectorConnectResultKind = "pending" ) +// Availability. +// Experimental: ConnectorDiscoveryAvailability is part of an experimental API and may +// change or be removed. +type ConnectorDiscoveryAvailability string + +const ( + // Disabled. + ConnectorDiscoveryAvailabilityDisabled ConnectorDiscoveryAvailability = "disabled" + // Enabled. + ConnectorDiscoveryAvailabilityEnabled ConnectorDiscoveryAvailability = "enabled" + // Unavailable. + ConnectorDiscoveryAvailabilityUnavailable ConnectorDiscoveryAvailability = "unavailable" +) + // Live MCP status of one Connector-owned runtime server. // Experimental: ConnectorMCPStatus is part of an experimental API and may change or be // removed. @@ -22025,6 +23861,44 @@ const ( CopilotAPITokenAuthInfoHostHTTPSGitHubCom CopilotAPITokenAuthInfoHost = "https://github.com" ) +// Result of reloading a customization component. +// Experimental: CustomizationReloadStatus is part of an experimental API and may change or +// be removed. +type CustomizationReloadStatus string + +const ( + // The component could not be refreshed; other components may still reload. + CustomizationReloadStatusFailed CustomizationReloadStatus = "failed" + // The component was refreshed successfully. + CustomizationReloadStatusReloaded CustomizationReloadStatus = "reloaded" + // The component was not configured, loaded, or eligible for refresh. + CustomizationReloadStatusSkipped CustomizationReloadStatus = "skipped" +) + +// Component of session customization discovery. +// Experimental: CustomizationReloadSubsystem is part of an experimental API and may change +// or be removed. +type CustomizationReloadSubsystem string + +const ( + // Discovered custom agents. + CustomizationReloadSubsystemAgents CustomizationReloadSubsystem = "agents" + // Configured session extensions. + CustomizationReloadSubsystemExtensions CustomizationReloadSubsystem = "extensions" + // Configured session and plugin hooks. + CustomizationReloadSubsystemHooks CustomizationReloadSubsystem = "hooks" + // Session instructions and their cached dynamic context. + CustomizationReloadSubsystemInstructions CustomizationReloadSubsystem = "instructions" + // Loaded MCP server configuration. + CustomizationReloadSubsystemMCP CustomizationReloadSubsystem = "mcp" + // Discovered plugin configuration. + CustomizationReloadSubsystemPlugins CustomizationReloadSubsystem = "plugins" + // Repository metadata and working-directory context. + CustomizationReloadSubsystemRepositoryContext CustomizationReloadSubsystem = "repositoryContext" + // Discovered skills. + CustomizationReloadSubsystemSkills CustomizationReloadSubsystem = "skills" +) + // Kind discriminator for DebugCollectLogsDestination. type DebugCollectLogsDestinationKind string @@ -22199,31 +24073,6 @@ const ( DiscoveredMCPServerTypeStdio DiscoveredMCPServerType = "stdio" ) -// Status discriminator for EntraTokenAcquireResult. -// Experimental: EntraTokenAcquireResultStatus is part of an experimental API and may change -// or be removed. -type EntraTokenAcquireResultStatus string - -const ( - EntraTokenAcquireResultStatusInteractionRequired EntraTokenAcquireResultStatus = "interaction-required" - EntraTokenAcquireResultStatusOk EntraTokenAcquireResultStatus = "ok" -) - -// How far OneAuth may go to acquire the requested token. -// Experimental: EntraTokenInteraction is part of an experimental API and may change or be -// removed. -type EntraTokenInteraction string - -const ( - // Always prompt interactively, bypassing any cached or silently-refreshable token. - EntraTokenInteractionForceInteractive EntraTokenInteraction = "force-interactive" - // Allow interactive acquisition, prompting the user only when a cached or silent token is - // unavailable. - EntraTokenInteractionInteractive EntraTokenInteraction = "interactive" - // Acquire the token without any user interaction, failing if interaction would be required. - EntraTokenInteractionSilent EntraTokenInteraction = "silent" -) - // GitHub Mission Control compute kind. // Experimental: EnvironmentKind is part of an experimental API and may change or be removed. type EnvironmentKind string @@ -22731,6 +24580,27 @@ const ( LoginProviderKindProxima LoginProviderKind = "proxima" ) +// What retrying did for one plugin required by managed settings. +// Experimental: ManagedPluginRetryStatus is part of an experimental API and may change or +// be removed. +type ManagedPluginRetryStatus string + +const ( + // The plugin was already installed and current, so nothing was done. + ManagedPluginRetryStatusAlreadyPresent ManagedPluginRetryStatus = "already_present" + // The plugin was not prepared because another session was changing plugins or the managed + // settings changed during the retry. It is prepared again before a later message. + ManagedPluginRetryStatusDeferred ManagedPluginRetryStatus = "deferred" + // Checking, installing, or updating the plugin failed. See `error`. + ManagedPluginRetryStatusFailed ManagedPluginRetryStatus = "failed" + // The plugin was missing and is now installed. + ManagedPluginRetryStatusInstalled ManagedPluginRetryStatus = "installed" + // The session's managed settings do not require this plugin, so nothing was done. + ManagedPluginRetryStatusNotRequired ManagedPluginRetryStatus = "not_required" + // An update was available and is now installed. + ManagedPluginRetryStatusUpdated ManagedPluginRetryStatus = "updated" +) + // A channel accepted by managedSettings.compose. // Experimental: ManagedSettingsChannel is part of an experimental API and may change or be // removed. @@ -23368,6 +25238,17 @@ const ( MCPPlanValueCategoryURLVariable MCPPlanValueCategory = "url-variable" ) +// The sender role of an MCP prompt message. +// Experimental: MCPPromptRole is part of an experimental API and may change or be removed. +type MCPPromptRole string + +const ( + // A message from the assistant. + MCPPromptRoleAssistant MCPPromptRole = "assistant" + // A message from the user. + MCPPromptRoleUser MCPPromptRole = "user" +) + // 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. @@ -23482,11 +25363,13 @@ const ( MCPServerConfigStdioTypeStdio MCPServerConfigStdioType = "stdio" ) -// Configuration source: user, workspace, plugin, builtin, or managed +// Configuration source: user, workspace, plugin, builtin, managed, or account // Experimental: MCPServerSource is part of an experimental API and may change or be removed. type MCPServerSource string const ( + // Server contributed by a signed-in account; enablement and organization policy still apply. + MCPServerSourceAccount MCPServerSource = "account" // Server bundled with the runtime. MCPServerSourceBuiltin MCPServerSource = "builtin" // Server supplied by a trusted host-managed catalog. @@ -23659,6 +25542,53 @@ const ( ModelPolicyStateUnconfigured ModelPolicyState = "unconfigured" ) +// When the runtime may run an adapter without an explicit user action. +// Experimental: ModelProviderAutomaticDiscoveryMode is part of an experimental API and may +// change or be removed. +type ModelProviderAutomaticDiscoveryMode string + +const ( + // The adapter declares that automatic discovery is safe when the other policy fields are + // satisfied. + ModelProviderAutomaticDiscoveryModeAutomatic ModelProviderAutomaticDiscoveryMode = "automatic" + // The adapter may refresh instances the user already configured, but must not scan for new + // instances automatically. + ModelProviderAutomaticDiscoveryModeConfiguredOnly ModelProviderAutomaticDiscoveryMode = "configuredOnly" + // The adapter must run only after an explicit user action. + ModelProviderAutomaticDiscoveryModeExplicit ModelProviderAutomaticDiscoveryMode = "explicit" +) + +// Whether a planned configuration entry is new or already present in the session registry. +// Experimental: ModelProviderConfigurationDisposition is part of an experimental API and +// may change or be removed. +type ModelProviderConfigurationDisposition string + +const ( + // An equivalent entry is already registered; the caller should reuse it rather than adding + // a duplicate. + ModelProviderConfigurationDispositionAlreadyConfigured ModelProviderConfigurationDisposition = "alreadyConfigured" + // No matching entry is registered; the caller should add the entry. + ModelProviderConfigurationDispositionCreate ModelProviderConfigurationDisposition = "create" +) + +// Network reach an adapter may use during discovery. +// Experimental: ModelProviderDiscoveryNetworkScope is part of an experimental API and may +// change or be removed. +type ModelProviderDiscoveryNetworkScope string + +const ( + // Discovery contacts only endpoints the user already configured. + ModelProviderDiscoveryNetworkScopeConfiguredEndpointOnly ModelProviderDiscoveryNetworkScope = "configuredEndpointOnly" + // Discovery may contact remote internet services. + ModelProviderDiscoveryNetworkScopeInternet ModelProviderDiscoveryNetworkScope = "internet" + // Discovery may scan or contact the local network. + ModelProviderDiscoveryNetworkScopeLocalNetwork ModelProviderDiscoveryNetworkScope = "localNetwork" + // Discovery is limited to loopback addresses on the local machine. + ModelProviderDiscoveryNetworkScopeLoopbackOnly ModelProviderDiscoveryNetworkScope = "loopbackOnly" + // Discovery does not contact a network service. + ModelProviderDiscoveryNetworkScopeNone ModelProviderDiscoveryNetworkScope = "none" +) + // The neutral kind of a model provider — the model analog of `AccountKind`. A model // provider is the live, entitled source a model came from; central code never branches on // this beyond a single dispatch. @@ -23675,6 +25605,40 @@ const ( ModelProviderKindLoki ModelProviderKind = "loki" ) +// Typed outcome for a provider operation. +// Experimental: ModelProviderOperationOutcomeCode is part of an experimental API and may +// change or be removed. +type ModelProviderOperationOutcomeCode string + +const ( + // The provider or instance is absent during discovery, status, or model listing. Distinct + // from a successful empty inventory. + ModelProviderOperationOutcomeCodeAbsent ModelProviderOperationOutcomeCode = "absent" + // The operation failed for a reason other than absence or reachability. + ModelProviderOperationOutcomeCodeFailed ModelProviderOperationOutcomeCode = "failed" + // The operation completed successfully; an empty inventory is valid. + ModelProviderOperationOutcomeCodeSuccess ModelProviderOperationOutcomeCode = "success" + // The provider is configured or expected but could not be reached. + ModelProviderOperationOutcomeCodeUnreachable ModelProviderOperationOutcomeCode = "unreachable" +) + +// Kind of component that supplied a provider adapter or row. Attribution does not confer +// authority. +// Experimental: ModelProviderProvenanceSource is part of an experimental API and may change +// or be removed. +type ModelProviderProvenanceSource string + +const ( + // Built into the runtime. + ModelProviderProvenanceSourceBuiltIn ModelProviderProvenanceSource = "builtIn" + // Derived from existing user configuration. + ModelProviderProvenanceSourceConfigured ModelProviderProvenanceSource = "configured" + // Supplied by another trusted contributor. + ModelProviderProvenanceSourceCustom ModelProviderProvenanceSource = "custom" + // Supplied by an extension. + ModelProviderProvenanceSourceExtension ModelProviderProvenanceSource = "extension" +) + // Whether the requested preference was already effective or was accepted for later // transactional activation. // Experimental: ModelSwitchAutoTierStatus is part of an experimental API and may change or @@ -24191,6 +26155,28 @@ const ( ProtocolSystemMessageConfigModeReplace ProtocolSystemMessageConfigMode = "replace" ) +// The product serving the model, reported in telemetry as `model_provider`. +// Experimental: ProviderConfigModelProvider is part of an experimental API and may change +// or be removed. +type ProviderConfigModelProvider string + +const ( + // Anthropic API. + ProviderConfigModelProviderAnthropic ProviderConfigModelProvider = "anthropic" + // Azure OpenAI Service. + ProviderConfigModelProviderAzureOpenai ProviderConfigModelProvider = "azure_openai" + // Foundry Local. + ProviderConfigModelProviderFoundryLocal ProviderConfigModelProvider = "foundry_local" + // llama.cpp server. + ProviderConfigModelProviderLlamaCpp ProviderConfigModelProvider = "llama_cpp" + // LM Studio. + ProviderConfigModelProviderLmStudio ProviderConfigModelProvider = "lm_studio" + // Ollama. + ProviderConfigModelProviderOllama ProviderConfigModelProvider = "ollama" + // OpenAI API. + ProviderConfigModelProviderOpenai ProviderConfigModelProvider = "openai" +) + // Provider transport. Defaults to "http". // Experimental: ProviderConfigTransport is part of an experimental API and may change or be // removed. @@ -24267,6 +26253,108 @@ const ( ProviderEndpointWireAPIResponses ProviderEndpointWireAPI = "responses" ) +// Access policy reported by the quota service. +// Experimental: ProviderQuotaAccessState is part of an experimental API and may change or +// be removed. +type ProviderQuotaAccessState string + +const ( + // Access is allowed. + ProviderQuotaAccessStateAllowed ProviderQuotaAccessState = "allowed" + // Access is denied. + ProviderQuotaAccessStateDenied ProviderQuotaAccessState = "denied" + // Quota is not required for this access. + ProviderQuotaAccessStateNotRequired ProviderQuotaAccessState = "not_required" + // Client-only compatibility placeholder when acquisitionStatus is unavailable or failed; + // not an observed service verdict. + ProviderQuotaAccessStateUnavailable ProviderQuotaAccessState = "unavailable" + // The service did not supply a recognized access state. + ProviderQuotaAccessStateUnknown ProviderQuotaAccessState = "unknown" +) + +// Acquisition state, independent of the service's business access/capacity verdict. +// Experimental: ProviderQuotaAcquisitionStatus is part of an experimental API and may +// change or be removed. +type ProviderQuotaAcquisitionStatus string + +const ( + // Acquisition failed; inspect httpStatus/acquisitionError without inferring a business + // verdict. + ProviderQuotaAcquisitionStatusFailed ProviderQuotaAcquisitionStatus = "failed" + // A service quota observation was received. + ProviderQuotaAcquisitionStatusSucceeded ProviderQuotaAcquisitionStatus = "succeeded" + // No service snapshot is available for this caller or surface. + ProviderQuotaAcquisitionStatusUnavailable ProviderQuotaAcquisitionStatus = "unavailable" +) + +// Capacity is independent of whether a numeric balance was supplied. +// Experimental: ProviderQuotaCapacityState is part of an experimental API and may change or +// be removed. +type ProviderQuotaCapacityState string + +const ( + // Capacity is available. + ProviderQuotaCapacityStateAvailable ProviderQuotaCapacityState = "available" + // The service explicitly reports exhaustion. + ProviderQuotaCapacityStateExhausted ProviderQuotaCapacityState = "exhausted" + // The service reports that capacity is not applicable. + ProviderQuotaCapacityStateNotApplicable ProviderQuotaCapacityState = "not_applicable" + // No billable quota is required. + ProviderQuotaCapacityStateNotRequired ProviderQuotaCapacityState = "not_required" + // Client-only compatibility placeholder when acquisitionStatus is unavailable or failed; + // not an observed service verdict. + ProviderQuotaCapacityStateUnavailable ProviderQuotaCapacityState = "unavailable" + // No recognized capacity state was supplied. + ProviderQuotaCapacityStateUnknown ProviderQuotaCapacityState = "unknown" + // The reported entitlement is unlimited. + ProviderQuotaCapacityStateUnlimited ProviderQuotaCapacityState = "unlimited" +) + +// Timing and purpose of a provider quota observation. +// Experimental: ProviderQuotaObservationKind is part of an experimental API and may change +// or be removed. +type ProviderQuotaObservationKind string + +const ( + // An explicit account read; consumption may still be subject to service aggregation delay. + ProviderQuotaObservationKindAccountSnapshot ProviderQuotaObservationKind = "account_snapshot" + // A verdict attached before an inference response. May be cached; never a post-charge + // balance. + ProviderQuotaObservationKindAdmissionState ProviderQuotaObservationKind = "admission_state" +) + +// Interpretation permitted for independently reported quantities. +// Experimental: ProviderQuotaQuantityKind is part of an experimental API and may change or +// be removed. +type ProviderQuotaQuantityKind string + +const ( + // Raw advisory quantities; do not infer permission, usage percentage, reset cadence, or + // post-charge balance. + ProviderQuotaQuantityKindAdvisoryBalance ProviderQuotaQuantityKind = "advisory_balance" + // Quantities form an authoritative available-of-entitled budget suitable for percentage + // presentation. + ProviderQuotaQuantityKindAuthoritativeBudget ProviderQuotaQuantityKind = "authoritative_budget" + // No quantity measurement, as with admission-only response headers. + ProviderQuotaQuantityKindNone ProviderQuotaQuantityKind = "none" +) + +// Units explicitly reported by a quota provider. +// Experimental: ProviderQuotaUnit is part of an experimental API and may change or be +// removed. +type ProviderQuotaUnit string + +const ( + // AI credits, not tokens or currency. + ProviderQuotaUnitAiCredits ProviderQuotaUnit = "ai_credits" + // Requests charged against an entitlement. + ProviderQuotaUnitRequests ProviderQuotaUnit = "requests" + // Model tokens. + ProviderQuotaUnitTokens ProviderQuotaUnit = "tokens" + // The observation does not establish a quantity unit. + ProviderQuotaUnitUnknown ProviderQuotaUnit = "unknown" +) + // Type of GitHub reference // Experimental: PushAttachmentGitHubReferenceType is part of an experimental API and may // change or be removed. @@ -24440,6 +26528,23 @@ const ( SandboxConfigSourceUserEnabled SandboxConfigSource = "user_enabled" ) +// State of the persistent certificate authority of the sandbox credential proxy. +// Experimental: SandboxProxyCaState is part of an experimental API and may change or be +// removed. +type SandboxProxyCaState string + +const ( + // The runtime could not read the certificate authority or the OS trust store. + SandboxProxyCaStateError SandboxProxyCaState = "error" + // OS trust includes the stored certificate authority. + SandboxProxyCaStateInstalled SandboxProxyCaState = "installed" + // OS trust does not include the certificate authority, or none is stored. + SandboxProxyCaStateNotInstalled SandboxProxyCaState = "notInstalled" + // This platform has no supported OS trust store. The proxy uses a per-process certificate + // bundle. + SandboxProxyCaStateUnsupported SandboxProxyCaState = "unsupported" +) + // A session-scoped sandbox transition applied while handling a slash command // Experimental: SandboxSessionChange is part of an experimental API and may change or be // removed. @@ -24766,6 +26871,30 @@ const ( SessionOpenParamsKindResumeLast SessionOpenParamsKind = "resumeLast" ) +// Copilot plan tier used by the session quota projection. +// Experimental: SessionQuotaPlanTier is part of an experimental API and may change or be +// removed. +type SessionQuotaPlanTier string + +const ( + // Copilot Business. + SessionQuotaPlanTierBusiness SessionQuotaPlanTier = "business" + // Copilot education entitlement. + SessionQuotaPlanTierEdu SessionQuotaPlanTier = "edu" + // Copilot Enterprise. + SessionQuotaPlanTierEnterprise SessionQuotaPlanTier = "enterprise" + // Copilot Free. + SessionQuotaPlanTierFree SessionQuotaPlanTier = "free" + // Copilot Max. + SessionQuotaPlanTierMax SessionQuotaPlanTier = "max" + // Copilot Pro. + SessionQuotaPlanTierPro SessionQuotaPlanTier = "pro" + // Copilot Pro+. + SessionQuotaPlanTierProPlus SessionQuotaPlanTier = "pro_plus" + // The account's plan tier is not known. + SessionQuotaPlanTierUnknown SessionQuotaPlanTier = "unknown" +) + // Status discriminator for SessionsClientMetadataEntry. type SessionsClientMetadataEntryStatus string @@ -24929,6 +27058,7 @@ const ( type SettableAuthInfoType string const ( + SettableAuthInfoTypeAccount SettableAuthInfoType = "account" SettableAuthInfoTypeAPIKey SettableAuthInfoType = "api-key" SettableAuthInfoTypeCopilotAPIToken SettableAuthInfoType = "copilot-api-token" SettableAuthInfoTypeEnv SettableAuthInfoType = "env" @@ -26038,6 +28168,82 @@ func (a *ServerCommandsAPI) List(ctx context.Context) (*CommandList, error) { return &result, nil } +// Experimental: ServerConnectorsAPI contains experimental APIs that may change or be +// removed. +type ServerConnectorsAPI serverAPI + +// GetAccounts returns eligible accounts. +// +// RPC method: connectors.getAccounts. +// +// Returns: Eligible accounts. +func (a *ServerConnectorsAPI) GetAccounts(ctx context.Context) (*ConnectorDiscoveryAccountList, error) { + raw, err := a.client.Request(ctx, "connectors.getAccounts", nil) + if err != nil { + return nil, err + } + var result ConnectorDiscoveryAccountList + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetCapabilities returns feature availability. +// +// RPC method: connectors.getCapabilities. +// +// Returns: Feature availability. +func (a *ServerConnectorsAPI) GetCapabilities(ctx context.Context) (*ConnectorDiscoveryCapabilities, error) { + raw, err := a.client.Request(ctx, "connectors.getCapabilities", nil) + if err != nil { + return nil, err + } + var result ConnectorDiscoveryCapabilities + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Lists entries for the selected account. +// +// RPC method: connectors.list. +// +// Parameters: Selected account. +// +// Returns: Entries for the selected account. +func (a *ServerConnectorsAPI) List(ctx context.Context, params *ConnectorDiscoveryAccountRequest) (*ConnectorDiscoveryCatalogResult, error) { + raw, err := a.client.Request(ctx, "connectors.list", params) + if err != nil { + return nil, err + } + var result ConnectorDiscoveryCatalogResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Refreshes entries for the selected account. +// +// RPC method: connectors.refresh. +// +// Parameters: Selected account. +// +// Returns: Entries for the selected account. +func (a *ServerConnectorsAPI) Refresh(ctx context.Context, params *ConnectorDiscoveryAccountRequest) (*ConnectorDiscoveryCatalogResult, error) { + raw, err := a.client.Request(ctx, "connectors.refresh", params) + if err != nil { + return nil, err + } + var result ConnectorDiscoveryCatalogResult + 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 @@ -26213,6 +28419,28 @@ func (a *ServerHostAPI) Dispose(ctx context.Context, params *HostDisposeRequest) return &result, nil } +// ListSessions reads the complete advertised live and dormant session catalog of an +// existing host owned by this connection. Does not start hosting, subscribe to sessions, or +// guarantee resumability. +// +// RPC method: host.listSessions. +// +// Parameters: Reads a running host's catalog or the runtime's saved default catalog. +// +// Returns: The complete live and dormant advertised catalog, without subscribing to +// sessions. +func (a *ServerHostAPI) ListSessions(ctx context.Context, params *HostListSessionsRequest) (*HostListSessionsResult, error) { + raw, err := a.client.Request(ctx, "host.listSessions", params) + if err != nil { + return nil, err + } + var result HostListSessionsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // PublishSession publishes an attached resident session for this listener's lifetime // without copying it. // @@ -27228,6 +29456,133 @@ func (a *ServerSandboxAPI) GetHostSupport(ctx context.Context) (*SandboxHostSupp return &result, nil } +// Experimental: ServerSandboxProxyCaAPI contains experimental APIs that may change or be +// removed. +type ServerSandboxProxyCaAPI serverAPI + +// 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. +// +// RPC method: sandbox.proxyCa.create. +// +// 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: Result of creating the persistent certificate authority of the sandbox +// credential proxy. +func (a *ServerSandboxProxyCaAPI) Create(ctx context.Context, params *SandboxProxyCaRequest) (*SandboxProxyCaCreateResult, error) { + raw, err := a.client.Request(ctx, "sandbox.proxyCa.create", params) + if err != nil { + return nil, err + } + var result SandboxProxyCaCreateResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetStatus 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. +// +// RPC method: sandbox.proxyCa.getStatus. +// +// 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: Status of the persistent certificate authority of the sandbox credential proxy. +func (a *ServerSandboxProxyCaAPI) GetStatus(ctx context.Context, params *SandboxProxyCaRequest) (*SandboxProxyCaStatus, error) { + raw, err := a.client.Request(ctx, "sandbox.proxyCa.getStatus", params) + if err != nil { + return nil, err + } + var result SandboxProxyCaStatus + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// 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. +// +// RPC method: sandbox.proxyCa.remove. +// +// Returns: Status of the persistent certificate authority of the sandbox credential proxy. +func (a *ServerSandboxProxyCaAPI) Remove(ctx context.Context) (*SandboxProxyCaStatus, error) { + raw, err := a.client.Request(ctx, "sandbox.proxyCa.remove", nil) + if err != nil { + return nil, err + } + var result SandboxProxyCaStatus + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Rotate 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. +// +// RPC method: sandbox.proxyCa.rotate. +// +// 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: Status of the persistent certificate authority of the sandbox credential proxy. +func (a *ServerSandboxProxyCaAPI) Rotate(ctx context.Context, params *SandboxProxyCaRequest) (*SandboxProxyCaStatus, error) { + raw, err := a.client.Request(ctx, "sandbox.proxyCa.rotate", params) + if err != nil { + return nil, err + } + var result SandboxProxyCaStatus + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Trust 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. +// +// RPC method: sandbox.proxyCa.trust. +// +// 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: Status of the persistent certificate authority of the sandbox credential proxy. +func (a *ServerSandboxProxyCaAPI) Trust(ctx context.Context, params *SandboxProxyCaRequest) (*SandboxProxyCaStatus, error) { + raw, err := a.client.Request(ctx, "sandbox.proxyCa.trust", params) + if err != nil { + return nil, err + } + var result SandboxProxyCaStatus + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: ProxyCa returns experimental APIs that may change or be removed. +func (s *ServerSandboxAPI) ProxyCa() *ServerSandboxProxyCaAPI { + return (*ServerSandboxProxyCaAPI)(s) +} + // Experimental: ServerSecretsAPI contains experimental APIs that may change or be removed. type ServerSecretsAPI serverAPI @@ -28152,17 +30507,15 @@ type ServerUserAPI serverAPI // removed. type ServerUserSettingsAPI serverAPI -// Get lists every known user setting (settings.json overlaid with the legacy config.json, -// config.json wins), 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. +// Get 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. // // RPC method: user.settings.get. // -// Returns: Per-key metadata for every known user setting (settings.json overlaid with the -// legacy config.json, config.json wins), including settings left at their default. Excludes -// repository- and enterprise-managed overrides. +// Returns: Per-key metadata for every known user setting in settings.json, including +// settings left at their default. Excludes repository- and enterprise-managed overrides. func (a *ServerUserSettingsAPI) Get(ctx context.Context) (*UserSettingsGetResult, error) { raw, err := a.client.Request(ctx, "user.settings.get", nil) if err != nil { @@ -28175,33 +30528,13 @@ func (a *ServerUserSettingsAPI) Get(ctx context.Context) (*UserSettingsGetResult return &result, nil } -// Reload drops this runtime process's in-memory user settings cache so the next settings -// read observes disk. -// -// RPC method: user.settings.reload. -func (a *ServerUserSettingsAPI) Reload(ctx context.Context) (*UserSettingsReloadResult, error) { - raw, err := a.client.Request(ctx, "user.settings.reload", nil) - if err != nil { - return nil, err - } - var result UserSettingsReloadResult - if err := json.Unmarshal(raw, &result); err != nil { - return nil, err - } - return &result, nil -} - // Set writes one or more user settings to settings.json, replacing each provided top-level -// key. A key whose value is null is removed. Returns the keys whose new value is shadowed -// by a legacy config.json entry (config.json wins on read), which the runtime leaves in -// place — such writes do not take effect until the legacy value is removed. +// key. A key whose value is null is removed. // // RPC method: user.settings.set. // // Parameters: 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. -// -// Returns: Outcome of writing user settings. func (a *ServerUserSettingsAPI) Set(ctx context.Context, params *UserSettingsSetRequest) (*UserSettingsSetResult, error) { raw, err := a.client.Request(ctx, "user.settings.set", params) if err != nil { @@ -28229,6 +30562,7 @@ type ServerRPC struct { Agents *ServerAgentsAPI Catalog *ServerCatalogAPI Commands *ServerCommandsAPI + Connectors *ServerConnectorsAPI Environments *ServerEnvironmentsAPI Extensions *ServerExtensionsAPI Hooks *ServerHooksAPI @@ -28297,6 +30631,7 @@ func NewServerRPC(client *jsonrpc2.Client) *ServerRPC { r.Agents = (*ServerAgentsAPI)(&r.common) r.Catalog = (*ServerCatalogAPI)(&r.common) r.Commands = (*ServerCommandsAPI)(&r.common) + r.Connectors = (*ServerConnectorsAPI)(&r.common) r.Environments = (*ServerEnvironmentsAPI)(&r.common) r.Extensions = (*ServerExtensionsAPI)(&r.common) r.Hooks = (*ServerHooksAPI)(&r.common) @@ -28322,33 +30657,440 @@ type internalServerAPI struct { client *jsonrpc2.Client } -// Experimental: InternalServerAccountsAPI contains experimental APIs that may change or be +// Experimental: InternalServerAgentsAPI contains experimental APIs that may change or be // removed. -type InternalServerAccountsAPI internalServerAPI +type InternalServerAgentsAPI internalServerAPI -// AcquireEntraToken acquire a Microsoft Entra access token through the runtime's OneAuth -// broker. Account-scoped because it uses the same native broker as the account stack: a -// trusted host application mints a scoped Entra token for its own use, most notably to -// authenticate to a remote MCP server whose authorization server is Entra ID (in place of -// the generic browser-OAuth flow). +// CustomAgentInitialModelDecision resolves the model a custom agent asks for against the +// models actually available, and answers both the model to switch to and the warning a user +// should see when the agent's preference cannot be met. A custom agent may name several +// acceptable models in preference order, so the decision is a match rather than a lookup, +// and an agent whose preference is unavailable is a normal outcome that produces a warning +// rather than an error. A host must call this rather than pick the first available name +// itself, because the preference order and the wording of the warning are what keep one +// installation's agent selection the same as another's. // -// RPC method: accounts.acquireEntraToken. +// RPC method: agents.customAgentInitialModelDecision. // -// Parameters: OneAuth token request supplied by a trusted host application. +// Parameters: The models a custom agent asks for, and the models actually available. // -// Returns: Result of a OneAuth token acquisition. -// Internal: AcquireEntraToken is part of the SDK's internal handshake/plumbing; external +// Returns: The model to switch to, and the warning to show when the agent's preference +// could not be met. +// Internal: CustomAgentInitialModelDecision is part of the SDK's internal +// handshake/plumbing; external callers should not use it. +func (a *InternalServerAgentsAPI) CustomAgentInitialModelDecision(ctx context.Context, params *AgentsCustomAgentInitialModelDecisionParams) (*AgentsCustomAgentInitialModelDecisionResult, error) { + raw, err := a.client.Request(ctx, "agents.customAgentInitialModelDecision", params) + if err != nil { + return nil, err + } + var result AgentsCustomAgentInitialModelDecisionResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetAvailableBuiltins lists the shipped agents a client should offer right now, filtered +// by the feature flags it passes. `getBuiltins` names every agent the runtime knows about; +// some of those are gated, so a client rendering a picker wants this narrower list together +// with the description to show beside each name. +// +// RPC method: agents.getAvailableBuiltins. +// +// Parameters: The feature flags to evaluate shipped agents against. +// +// Returns: The shipped agents available under the requested flags. +// Internal: GetAvailableBuiltins is part of the SDK's internal handshake/plumbing; external // callers should not use it. -func (a *InternalServerAccountsAPI) AcquireEntraToken(ctx context.Context, params *EntraTokenAcquireRequest) (EntraTokenAcquireResult, error) { - raw, err := a.client.Request(ctx, "accounts.acquireEntraToken", params) +func (a *InternalServerAgentsAPI) GetAvailableBuiltins(ctx context.Context, params *AgentsGetAvailableBuiltinsRequest) (*AgentsGetAvailableBuiltinsResult, error) { + raw, err := a.client.Request(ctx, "agents.getAvailableBuiltins", params) if err != nil { return nil, err } - result, err := unmarshalEntraTokenAcquireResult(raw) + var result AgentsGetAvailableBuiltinsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetBuiltinDefinition loads one shipped agent's YAML definition, for a client that needs +// what the agent declares rather than only its name. `getBuiltins` reports which names have +// a definition to load: a name outside its `yamlBasedNames` is special-cased in code and +// has none. The definition crosses as its own JSON rather than as contract-typed fields, +// because the runtime parses it with the agent schema's tolerant shape and re-typing it +// here would drop the keys that shape accepts and this one does not. The projected +// `__nativeCustomAgent` view the runtime derives is included, so a caller reading the +// declared model and a caller rendering the agent see the same definition. +// +// RPC method: agents.getBuiltinDefinition. +// +// Parameters: The shipped agent whose definition to load. +// +// Returns: One shipped agent's definition. +// Internal: GetBuiltinDefinition is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerAgentsAPI) GetBuiltinDefinition(ctx context.Context, params *AgentsGetBuiltinDefinitionRequest) (*AgentsGetBuiltinDefinitionResult, error) { + raw, err := a.client.Request(ctx, "agents.getBuiltinDefinition", params) if err != nil { return nil, err } - return result, nil + var result AgentsGetBuiltinDefinitionResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetBuiltinListingDefinition projects one shipped agent the way a picker lists it, reading +// only the metadata at the head of the definition file and stopping before the prompt body. +// `getBuiltinDefinition` answers the whole definition instead, so a client listing every +// shipped agent should prefer this one: the cost of a listing grows with the number of +// agents, and the prompt body is the part a listing never shows. The two also differ in +// shape. This returns the projected custom agent on its own, whereas `getBuiltinDefinition` +// returns the authored definition with that projection nested under `__nativeCustomAgent`. +// +// RPC method: agents.getBuiltinListingDefinition. +// +// Parameters: The shipped agent whose listing entry to load. +// +// Returns: One shipped agent, projected for a listing. +// Internal: GetBuiltinListingDefinition is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerAgentsAPI) GetBuiltinListingDefinition(ctx context.Context, params *AgentsGetBuiltinListingDefinitionRequest) (*AgentsGetBuiltinListingDefinitionResult, error) { + raw, err := a.client.Request(ctx, "agents.getBuiltinListingDefinition", params) + if err != nil { + return nil, err + } + var result AgentsGetBuiltinListingDefinitionResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetBuiltins 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. +// +// RPC method: agents.getBuiltins. +// +// Returns: The agents this runtime ships, named so a consumer can tell them apart from +// authored ones. +// Internal: GetBuiltins is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerAgentsAPI) GetBuiltins(ctx context.Context) (*AgentsGetBuiltinsResult, error) { + raw, err := a.client.Request(ctx, "agents.getBuiltins", nil) + if err != nil { + return nil, err + } + var result AgentsGetBuiltinsResult + 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 + +// CurrentBranchRemote 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. +// +// RPC method: git.currentBranchRemote. +// +// Parameters: Working-tree path a git query applies to. +// +// Returns: The remote the checked-out branch tracks. +// Internal: CurrentBranchRemote is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerGitAPI) CurrentBranchRemote(ctx context.Context, params *GitCwdRequest) (*GitCurrentBranchRemoteResult, error) { + raw, err := a.client.Request(ctx, "git.currentBranchRemote", params) + if err != nil { + return nil, err + } + var result GitCurrentBranchRemoteResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// ReposFromRemotes 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. +// +// RPC method: git.reposFromRemotes. +// +// Parameters: Git working tree whose GitHub remotes should be listed. +// +// Returns: The GitHub repositories a working tree's remotes point at. +// Internal: ReposFromRemotes is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerGitAPI) ReposFromRemotes(ctx context.Context, params *GitReposFromRemotesRequest) (*GitReposFromRemotesResult, error) { + raw, err := a.client.Request(ctx, "git.reposFromRemotes", params) + if err != nil { + return nil, err + } + var result GitReposFromRemotesResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// WorkingDirectoryContext 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. +// +// RPC method: git.workingDirectoryContext. +// +// Parameters: Working-tree path a git query applies to. +// +// Returns: Updated working directory and git context. Emitted as the new payload of +// `session.context_changed`. +// Internal: WorkingDirectoryContext is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerGitAPI) WorkingDirectoryContext(ctx context.Context, params *GitCwdRequest) (*SessionWorkingDirectoryContext, error) { + raw, err := a.client.Request(ctx, "git.workingDirectoryContext", params) + if err != nil { + return nil, err + } + var result SessionWorkingDirectoryContext + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalServerGitHubOwnersAPI contains experimental APIs that may change or +// be removed. +type InternalServerGitHubOwnersAPI internalServerAPI + +// Cancel abandons an owner listing started with the given request id. Answers `canceled: +// true` while a listing with that id is running. Answers `canceled: false` when the id was +// never registered, was registered but not used, was released after being abandoned, or its +// listing has ended. Canceling an unused id releases it, and a later `list` with that id is +// refused. The cancel acts only on owner listings and never reaches another request of the +// host. +// +// RPC method: gitHubOwners.cancel. +// +// Parameters: The owner listing to abandon. +// +// Returns: Whether the id named a running owner listing. +// Internal: Cancel is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerGitHubOwnersAPI) Cancel(ctx context.Context, params *GitHubOwnersCancelRequest) (*GitHubOwnersCancelResult, error) { + raw, err := a.client.Request(ctx, "gitHubOwners.cancel", params) + if err != nil { + return nil, err + } + var result GitHubOwnersCancelResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// 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`. +// +// RPC method: gitHubOwners.list. +// +// Parameters: Credential to list owners under, and the request id that makes the listing +// cancellable. +// +// Returns: 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: List is part of the SDK's internal handshake/plumbing; external callers should +// not use it. +func (a *InternalServerGitHubOwnersAPI) List(ctx context.Context, params *GitHubOwnersListRequest) (*GitHubOwnersListResult, error) { + raw, err := a.client.Request(ctx, "gitHubOwners.list", params) + if err != nil { + return nil, err + } + var result GitHubOwnersListResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// NextRequestId 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. +// +// RPC 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. +// Internal: NextRequestId is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerGitHubOwnersAPI) NextRequestId(ctx context.Context) (*GitHubOwnersRequestIDResult, error) { + raw, err := a.client.Request(ctx, "gitHubOwners.nextRequestId", nil) + if err != nil { + return nil, err + } + var result GitHubOwnersRequestIDResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalServerGitHubRepositoryAPI contains experimental APIs that may +// change or be removed. +type InternalServerGitHubRepositoryAPI internalServerAPI + +// AtPath resolves the GitHub repository that owns a working-tree path by reading the +// selected git remote configured for it, preferring `origin`. Returns a null `repository` +// when the path is inside a git working tree but that selected remote does not resolve to a +// GitHub host. Fails when the path is not inside a git working tree at all, so a caller can +// tell 'not a repository' apart from 'a repository with no GitHub remote'. +// +// RPC method: gitHubRepository.atPath. +// +// Parameters: Working-tree path whose owning GitHub repository should be resolved. +// +// Returns: The GitHub repository that owns the requested path, when the selected remote +// (`origin`, else the first) is on a GitHub host. +// Internal: AtPath is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerGitHubRepositoryAPI) AtPath(ctx context.Context, params *GitHubRepositoryAtPathRequest) (*GitHubRepositoryAtPathResult, error) { + raw, err := a.client.Request(ctx, "gitHubRepository.atPath", params) + if err != nil { + return nil, err + } + var result GitHubRepositoryAtPathResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalServerGlobalStateAPI contains experimental APIs that may change or +// be removed. +type InternalServerGlobalStateAPI internalServerAPI + +// Load reads the host's machine-wide state: which plugins are installed and the one-off +// flags and timestamps that record what the user has already been shown or migrated. This +// is the state that outlives a single session and a single workspace, so a host reads it to +// decide whether to run a first-launch step, offer an onboarding prompt, or skip one it has +// already completed. The stored credentials are deliberately not part of this result; a +// caller that needs an authenticated identity asks the account methods for it instead. +// Reading is non-destructive and every field is optional, because a fresh install has +// recorded nothing yet. +// +// RPC method: globalState.load. +// +// Returns: 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. +// Internal: Load is part of the SDK's internal handshake/plumbing; external callers should +// not use it. +func (a *InternalServerGlobalStateAPI) Load(ctx context.Context) (*GlobalStateLoadResult, error) { + raw, err := a.client.Request(ctx, "globalState.load", nil) + if err != nil { + return nil, err + } + var result GlobalStateLoadResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// LoadForConfigDir reads the host's machine-wide state exactly as `globalState.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 state read matches the directory that session +// actually uses. An absent or empty `configDir` resolves the server's own home, making this +// identical to `globalState.load`. The stored credentials are omitted here for the same +// reason they are omitted from `globalState.load`: a caller that needs an authenticated +// identity asks the account methods instead, so pointing this at another directory cannot +// be used to read the credentials kept in it. +// +// RPC method: globalState.loadForConfigDir. +// +// Parameters: Selects the configuration directory whose machine-wide state to read. +// +// Returns: 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. +// Internal: LoadForConfigDir is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerGlobalStateAPI) LoadForConfigDir(ctx context.Context, params *GlobalStateLoadForConfigDirRequest) (*GlobalStateLoadResult, error) { + raw, err := a.client.Request(ctx, "globalState.loadForConfigDir", params) + if err != nil { + return nil, err + } + var result GlobalStateLoadResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// WriteKey records one top-level key in the host's machine-wide state, the counterpart to +// `globalState.load`. A host calls this to remember that it has shown an onboarding step, +// asked a one-off question, or completed a migration, so the next run can skip it. Only the +// named key is replaced and the rest of the document is preserved, which lets two writers +// record different flags without overwriting each other; passing no value removes the key +// instead. Only the keys a host records itself are writable: `appInstallNudgeResponded`, +// `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, +// `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every +// other key is refused, including `installedPlugins`, the stored credentials, +// `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use +// the plugin APIs, which apply repository and managed-policy checks. +// +// RPC method: globalState.writeKey. +// +// Parameters: 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. +// Internal: WriteKey is part of the SDK's internal handshake/plumbing; external callers +// should not use it. +func (a *InternalServerGlobalStateAPI) WriteKey(ctx context.Context, params *GlobalStateWriteKeyRequest) (*GlobalStateWriteKeyResult, error) { + raw, err := a.client.Request(ctx, "globalState.writeKey", params) + if err != nil { + return nil, err + } + var result GlobalStateWriteKeyResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil } // Experimental: InternalServerHostAPI contains experimental APIs that may change or be @@ -28489,6 +31231,33 @@ func (a *InternalServerSessionsAPI) ConfigureSessionExtensions(ctx context.Conte return &result, nil } +// CreateWorkspace 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. +// +// RPC method: sessions.createWorkspace. +// +// Parameters: Identity, state location and starting context for a workspace record. +// +// Returns: The workspace record that was written. +// Internal: CreateWorkspace is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerSessionsAPI) CreateWorkspace(ctx context.Context, params *SessionsCreateWorkspaceRequest) (*SessionsCreateWorkspaceResult, error) { + raw, err := a.client.Request(ctx, "sessions.createWorkspace", params) + if err != nil { + return nil, err + } + var result SessionsCreateWorkspaceResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Deletes one local session from disk after running the same lifecycle hooks as the session // manager. // @@ -28629,15 +31398,73 @@ func (a *InternalServerSessionsAPI) ListNonEmptySessionIds(ctx context.Context, return &result, nil } +// LoadWorkspace 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. +// +// RPC method: sessions.loadWorkspace. +// +// Parameters: Where the session's state lives, as a root directory and the session ID under +// it. +// +// Returns: The workspace record on disk, omitted when the session has none. +// Internal: LoadWorkspace is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalServerSessionsAPI) LoadWorkspace(ctx context.Context, params *SessionsLoadWorkspaceRequest) (*SessionsLoadWorkspaceResult, error) { + raw, err := a.client.Request(ctx, "sessions.loadWorkspace", params) + if err != nil { + return nil, err + } + var result SessionsLoadWorkspaceResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// UpdateWorkspaceFields 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`. +// +// RPC method: sessions.updateWorkspaceFields. +// +// Parameters: Where the session's state lives, plus workspace-schema fields to merge into +// its workspace record. Stored keys outside the schema are not preserved, and a stored +// `fork_count` is never replaced. +// +// Returns: The merge completed. The record carries the supplied workspace-schema fields, +// but a stored `fork_count` stays. +// Internal: UpdateWorkspaceFields is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalServerSessionsAPI) UpdateWorkspaceFields(ctx context.Context, params *SessionsUpdateWorkspaceFieldsRequest) (*SessionsUpdateWorkspaceFieldsResult, error) { + raw, err := a.client.Request(ctx, "sessions.updateWorkspaceFields", params) + if err != nil { + return nil, err + } + var result SessionsUpdateWorkspaceFieldsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // 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 - Accounts *InternalServerAccountsAPI - Host *InternalServerHostAPI - Sessions *InternalServerSessionsAPI + Agents *InternalServerAgentsAPI + Git *InternalServerGitAPI + GitHubOwners *InternalServerGitHubOwnersAPI + GitHubRepository *InternalServerGitHubRepositoryAPI + GlobalState *InternalServerGlobalStateAPI + Host *InternalServerHostAPI + Sessions *InternalServerSessionsAPI } // Connect performs the SDK server connection handshake and validates the optional @@ -28672,7 +31499,11 @@ func (a *InternalServerRPC) Connect(ctx context.Context, params *ConnectRequest) func NewInternalServerRPC(client *jsonrpc2.Client) *InternalServerRPC { r := &InternalServerRPC{} r.common = internalServerAPI{client: client} - r.Accounts = (*InternalServerAccountsAPI)(&r.common) + r.Agents = (*InternalServerAgentsAPI)(&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.Sessions = (*InternalServerSessionsAPI)(&r.common) return r @@ -29632,21 +32463,24 @@ func (a *ContentExclusionAPI) CheckPaths(ctx context.Context, params *ContentExc // Experimental: CustomizationsAPI contains experimental APIs that may change or be removed. type CustomizationsAPI sessionAPI -// Reloads all repository and user customizations for the active session: instructions, -// plugins and their MCP servers and hooks, custom agents, extensions, and skills. Returns -// diagnostics from the final skill reload. +// Reload for local sessions, reconciles repository context and discovered instructions, +// plugins, skills, agents, hooks, MCP servers, and extensions after files appear or change +// under the working directory. Independent component failures are returned in outcomes and +// errors; a rejected call can have partially applied earlier steps. Remote sessions must +// reload on their agent host instead. The model-facing context is rebuilt on the next turn. // // RPC method: session.customizations.reload. // -// Returns: Diagnostics from reloading skill definitions, with warnings and errors as -// separate lists. -func (a *CustomizationsAPI) Reload(ctx context.Context) (*SkillsLoadDiagnostics, error) { +// Returns: 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. +func (a *CustomizationsAPI) Reload(ctx context.Context) (*CustomizationsReloadResult, error) { req := map[string]any{"sessionId": a.sessionID} raw, err := a.client.Request(ctx, "session.customizations.reload", req) if err != nil { return nil, err } - var result SkillsLoadDiagnostics + var result CustomizationsReloadResult if err := json.Unmarshal(raw, &result); err != nil { return nil, err } @@ -29873,7 +32707,9 @@ func (a *EventLogAPI) Tail(ctx context.Context) (*EventLogTailResult, error) { // Experimental: ExtensionsAPI contains experimental APIs that may change or be removed. type ExtensionsAPI sessionAPI -// Disables an extension for the session. +// Disables an extension for the session and persists the preference when the session has a +// settings store. Hosts synchronizing effective membership should use extensions.reconcile +// instead. // // RPC method: session.extensions.disable. // @@ -29894,7 +32730,9 @@ func (a *ExtensionsAPI) Disable(ctx context.Context, params *ExtensionsDisableRe return &result, nil } -// Enables an extension for the session. +// Enables an extension for the session and persists the preference when the session has a +// settings store. Hosts synchronizing effective membership should use extensions.reconcile +// instead. // // RPC method: session.extensions.enable. // @@ -29933,6 +32771,38 @@ func (a *ExtensionsAPI) List(ctx context.Context) (*ExtensionList, error) { return &result, nil } +// Reconcile host-only reconciliation of authoritative session-effective extension +// membership and enablement. Refreshes runtime-owned discovery and preferences without +// persisting settings, installing plugins, or restarting unchanged activations. Returns +// ExtensionList only after required starts and process/contribution cleanup settle. Takes +// no caller inventory or overrides. Missing controllers, unready/incomplete discovery, +// unavailable workspaces, superseded inputs, and lifecycle failures are errors, not empty +// membership. Independently proven revocations may be applied before an error; retry +// converges without restarting healthy activations. Error data contains +// lifecycleChangesApplied and code: extension_reconciliation_host_required, +// extension_reconciliation_unavailable, extension_reconciliation_not_ready, +// extension_reconciliation_discovery_failed, +// extension_reconciliation_workspace_unavailable, extension_reconciliation_superseded, or +// extension_reconciliation_lifecycle_failed. Mark host reconciliation state applied only on +// success. On older runtimes, method-not-found must not fall back to global discovery and +// persistent extension disables. +// +// RPC method: session.extensions.reconcile. +// +// Returns: Extensions discovered for the session, with their current status. +func (a *ExtensionsAPI) Reconcile(ctx context.Context) (*ExtensionList, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request(ctx, "session.extensions.reconcile", req) + if err != nil { + return nil, err + } + var result ExtensionList + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Reloads extension definitions and processes for the session. // // RPC method: session.extensions.reload. @@ -30307,17 +33177,20 @@ func (a *InstructionsAPI) GetSources(ctx context.Context) (*InstructionsGetSourc return &result, nil } -// Reload invalidates cached custom-instruction discovery so subsequent turns and source -// reads observe instruction files currently on disk. +// Reload for local sessions, invalidates instruction discovery and the model-facing prompt, +// then returns freshly discovered sources. The updated prompt takes effect on the next +// turn. Remote sessions must reload on their agent host instead. // // RPC method: session.instructions.reload. -func (a *InstructionsAPI) Reload(ctx context.Context) (*SessionInstructionsReloadResult, error) { +// +// Returns: Instruction sources loaded for the session, in merge order. +func (a *InstructionsAPI) Reload(ctx context.Context) (*InstructionsGetSourcesResult, error) { req := map[string]any{"sessionId": a.sessionID} raw, err := a.client.Request(ctx, "session.instructions.reload", req) if err != nil { return nil, err } - var result SessionInstructionsReloadResult + var result InstructionsGetSourcesResult if err := json.Unmarshal(raw, &result); err != nil { return nil, err } @@ -30550,10 +33423,8 @@ func (a *MCPAPI) IsServerRunning(ctx context.Context, params *MCPIsServerRunning return &result, nil } -// Lists MCP servers configured for the session, their connection status, and host-level -// state. The host-level state (disabled/filtered servers, failed/needs-auth/pending -// connections, mcp3p policy, full config) is empty/zero when no MCP host has been -// initialized for the session. +// Lists materialized MCP servers and their connection status. Cache misses may start and +// wait for MCP servers. // // RPC method: session.mcp.list. // @@ -30572,6 +33443,27 @@ func (a *MCPAPI) List(ctx context.Context) (*MCPServerList, error) { return &result, nil } +// ListConfigured lists effective MCP configuration without starting, restarting, +// authenticating, or waiting for servers. An optional live observation is from an already +// materialized matching server; this is not a readiness guarantee. +// +// RPC method: session.mcp.listConfigured. +// +// Returns: Effective MCP configuration with optional live observations from matching +// already materialized servers. +func (a *MCPAPI) ListConfigured(ctx context.Context) (*MCPConfiguredServerList, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request(ctx, "session.mcp.listConfigured", req) + if err != nil { + return nil, err + } + var result MCPConfiguredServerList + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // ListTools lists the tools exposed by a connected MCP server on this session's host. This // performs a live `tools/list` request. Tool UI metadata is returned independently of // whether MCP Apps rendering is enabled for the session. @@ -31014,6 +33906,29 @@ func (a *MCPOauthAPI) CancelLogin(ctx context.Context, params *SessionMCPOauthCa return &result, nil } +// Completes a runtime-managed MCP OAuth login after the authorization server redirects to a +// host-managed callback URL. +// +// RPC method: session.mcp.oauth.complete. +// +// Parameters: Host-delivered callback for a runtime-managed MCP OAuth login. +func (a *MCPOauthAPI) Complete(ctx context.Context, params *MCPOauthCompleteRequest) (*SessionMCPOauthCompleteResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["authorizationId"] = params.AuthorizationID + req["callbackUrl"] = params.CallbackURL + } + raw, err := a.client.Request(ctx, "session.mcp.oauth.complete", req) + if err != nil { + return nil, err + } + var result SessionMCPOauthCompleteResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // HandlePendingRequest resolves a pending MCP OAuth request with a host-provided token or // cancellation. The pending request is emitted as mcp.oauth_required with the data // necessary to authorize the request. @@ -31047,7 +33962,7 @@ func (a *MCPOauthAPI) HandlePendingRequest(ctx context.Context, params *MCPOauth // RPC method: session.mcp.oauth.login. // // Parameters: Remote MCP server name and optional overrides controlling reauthentication, -// OAuth client display name, callback success-page copy, and static OAuth client selection. +// OAuth client display name, callback handling, and static OAuth client selection. // // Returns: OAuth authorization URL the caller should open, or empty when cached tokens // already authenticated the server. @@ -31081,6 +33996,9 @@ func (a *MCPOauthAPI) Login(ctx context.Context, params *MCPOauthLoginRequest) ( if params.PublicClient != nil { req["publicClient"] = *params.PublicClient } + if params.RedirectURI != nil { + req["redirectUri"] = *params.RedirectURI + } req["serverName"] = params.ServerName } raw, err := a.client.Request(ctx, "session.mcp.oauth.login", req) @@ -31191,6 +34109,70 @@ func (s *MCPAPI) Oauth() *MCPOauthAPI { return (*MCPOauthAPI)(s) } +// Experimental: MCPPromptsAPI contains experimental APIs that may change or be removed. +type MCPPromptsAPI sessionAPI + +// Get a prompt's messages from a connected MCP server (proxies MCP `prompts/get`). Content +// is preserved as opaque JSON. Does not send messages to the model, execute tools, or fetch +// referenced resources. +// +// RPC method: session.mcp.prompts.get. +// +// Parameters: MCP server, prompt name, and optional string-valued arguments. +// +// Returns: Prompt messages returned by the MCP server without sending them to the model. +func (a *MCPPromptsAPI) Get(ctx context.Context, params *MCPPromptsGetRequest) (*MCPPromptsGetResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + if params.Arguments != nil { + req["arguments"] = params.Arguments + } + req["promptName"] = params.PromptName + req["serverName"] = params.ServerName + } + raw, err := a.client.Request(ctx, "session.mcp.prompts.get", req) + if err != nil { + return nil, err + } + var result MCPPromptsGetResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// List enumerate one page of prompts a connected MCP server exposes (proxies MCP +// `prompts/list`). Pass `cursor` to continue from a prior result's `nextCursor`. +// +// RPC method: session.mcp.prompts.list. +// +// Parameters: MCP server whose prompts to enumerate. +// +// Returns: One page of prompts advertised by the named MCP server. +func (a *MCPPromptsAPI) List(ctx context.Context, params *MCPPromptsListRequest) (*MCPPromptsListResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + if params.Cursor != nil { + req["cursor"] = *params.Cursor + } + req["serverName"] = params.ServerName + } + raw, err := a.client.Request(ctx, "session.mcp.prompts.list", req) + if err != nil { + return nil, err + } + var result MCPPromptsListResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Prompts returns experimental APIs that may change or be removed. +func (s *MCPAPI) Prompts() *MCPPromptsAPI { + return (*MCPPromptsAPI)(s) +} + // Experimental: MCPResourcesAPI contains experimental APIs that may change or be removed. type MCPResourcesAPI sessionAPI @@ -31313,6 +34295,9 @@ func (a *MetadataAPI) ContextInfo(ctx context.Context, params *MetadataContextIn if params != nil { req["outputTokenLimit"] = params.OutputTokenLimit req["promptTokenLimit"] = params.PromptTokenLimit + if params.ProviderID != nil { + req["providerId"] = *params.ProviderID + } if params.SelectedModel != nil { req["selectedModel"] = *params.SelectedModel } @@ -31640,6 +34625,9 @@ func (a *ModeAPI) Set(ctx context.Context, params *ModeSetRequest) (*ModeSetResu if params.PlanModelConfigured != nil { req["planModelConfigured"] = *params.PlanModelConfigured } + if params.PlanModelProviderID != nil { + req["planModelProviderId"] = *params.PlanModelProviderID + } if params.PlanReasoningEffort != nil { req["planReasoningEffort"] = *params.PlanReasoningEffort } @@ -31840,6 +34828,9 @@ func (a *ModelAPI) SwitchTo(ctx context.Context, params *ModelSwitchToRequest) ( if params.PickerPersistence != nil { req["pickerPersistence"] = *params.PickerPersistence } + if params.ProviderID != nil { + req["providerId"] = *params.ProviderID + } if params.ReasoningEffort != nil { req["reasoningEffort"] = *params.ReasoningEffort } @@ -32950,6 +35941,40 @@ func (a *PluginsAPI) Reload(ctx context.Context, params ...*SessionPluginsReload return &result, nil } +// RetryManaged installs or updates the plugins the session's managed settings require now, +// without waiting for the next automatic attempt after a failure, then reloads plugins. +// Reports progress and failures with the same `session.info` and `session.warning` events +// as automatic preparation. Fails when the runtime does not prepare managed plugins for the +// session. +// +// RPC method: session.plugins.retryManaged. +// +// Parameters: Plugins required by the session's managed settings to install or update again. +// +// Returns: Per-plugin outcomes of retrying the plugins required by the session's managed +// settings. +func (a *PluginsAPI) RetryManaged(ctx context.Context, params ...*SessionPluginsRetryManagedRequest) (*SessionPluginsRetryManagedResult, error) { + var requestParams *SessionPluginsRetryManagedRequest + if len(params) > 0 { + requestParams = params[0] + } + req := map[string]any{"sessionId": a.sessionID} + if requestParams != nil { + if requestParams.Plugins != nil { + req["plugins"] = requestParams.Plugins + } + } + raw, err := a.client.Request(ctx, "session.plugins.retryManaged", req) + if err != nil { + return nil, err + } + var result SessionPluginsRetryManagedResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Uninstalls a plugin when permitted by the live session's retained managed policy. // // RPC method: session.plugins.uninstall. @@ -33267,6 +36292,146 @@ func (a *ProviderAPI) Withdraw(ctx context.Context, params *ProviderWithdrawRequ return &result, nil } +// Experimental: ProvidersAPI contains experimental APIs that may change or be removed. +type ProvidersAPI sessionAPI + +// Discovers reachable instances using an adapter from this session's effective provider +// catalog and provider-specific discovery input. +// +// RPC method: session.providers.discover. +// +// Parameters: Provider discovery parameters. +// +// Returns: Provider instances found by a discovery operation. +func (a *ProvidersAPI) Discover(ctx context.Context, params *ModelProviderDiscoverRequest) (*ModelProviderDiscoverResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["adapterId"] = params.AdapterID + if params.Input != nil { + req["input"] = params.Input + } + } + raw, err := a.client.Request(ctx, "session.providers.discover", req) + if err != nil { + return nil, err + } + var result ModelProviderDiscoverResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetCatalog returns adapter definitions and supported operations in this session's +// effective provider catalog, without running discovery. Does not list provider instances +// or select inference models. +// +// RPC method: session.providers.getCatalog. +// +// Returns: Normalized model-provider adapter definitions available to the session, not +// discovered instances. +func (a *ProvidersAPI) GetCatalog(ctx context.Context) (*ModelProviderAdapterCatalog, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request(ctx, "session.providers.getCatalog", req) + if err != nil { + return nil, err + } + var result ModelProviderAdapterCatalog + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// GetStatus gets current health and version information for a discovered model-provider +// instance. +// +// RPC method: session.providers.getStatus. +// +// Parameters: Provider status request parameters. +// +// Returns: Current health information for a provider instance. +func (a *ProvidersAPI) GetStatus(ctx context.Context, params *ModelProviderGetStatusRequest) (*ModelProviderStatus, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["instance"] = params.Instance + } + raw, err := a.client.Request(ctx, "session.providers.getStatus", req) + if err != nil { + return nil, err + } + var result ModelProviderStatus + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: ProvidersModelsAPI contains experimental APIs that may change or be removed. +type ProvidersModelsAPI sessionAPI + +// Lists models installed or otherwise available from a discovered model-provider instance. +// +// RPC method: session.providers.models.list. +// +// Parameters: Provider model inventory request parameters. +// +// Returns: 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. +func (a *ProvidersModelsAPI) List(ctx context.Context, params *ModelProviderModelsListRequest) (*DiscoveredModelList, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["instance"] = params.Instance + } + raw, err := a.client.Request(ctx, "session.providers.models.list", req) + if err != nil { + return nil, err + } + var result DiscoveredModelList + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// PrepareConfiguration translates a discovered model into the provider and model +// configuration needed to use it, and reports whether each is already registered in this +// session. Prepares only: it registers nothing, writes nothing, and performs no provider +// requests. +// +// RPC method: session.providers.models.prepareConfiguration. +// +// Parameters: 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`. +// +// Returns: 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. +func (a *ProvidersModelsAPI) PrepareConfiguration(ctx context.Context, params *ModelProviderPrepareConfigurationRequest) (*ModelProviderConfigurationPlan, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["instance"] = params.Instance + req["model"] = params.Model + } + raw, err := a.client.Request(ctx, "session.providers.models.prepareConfiguration", req) + if err != nil { + return nil, err + } + var result ModelProviderConfigurationPlan + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: Models returns experimental APIs that may change or be removed. +func (s *ProvidersAPI) Models() *ProvidersModelsAPI { + return (*ProvidersModelsAPI)(s) +} + // Experimental: QueueAPI contains experimental APIs that may change or be removed. type QueueAPI sessionAPI @@ -33553,6 +36718,66 @@ func (a *QueueAPI) WithdrawMessage(ctx context.Context, params *QueueWithdrawMes return &result, nil } +// Experimental: QuotaAPI contains experimental APIs that may change or be removed. +type QuotaAPI sessionAPI + +// Gets the session's current quota and account projection without making a network request. +// +// RPC method: session.quota.get. +// +// Returns: The incumbent session-owned quota and account projection. +func (a *QuotaAPI) Get(ctx context.Context) (*SessionQuotaGetResult, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request(ctx, "session.quota.get", req) + if err != nil { + return nil, err + } + var result SessionQuotaGetResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Refreshes the session's provider model catalog bypassing its cache, folds quota snapshots +// into session state, and returns the updated projection. Failures leave the last known +// quota intact. +// +// RPC method: session.quota.refresh. +// +// Returns: The incumbent session-owned quota and account projection. +func (a *QuotaAPI) Refresh(ctx context.Context) (*SessionQuotaRefreshResult, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request(ctx, "session.quota.refresh", req) + if err != nil { + return nil, err + } + var result SessionQuotaRefreshResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// TakeWarnings returns and clears the session's pending quota warnings. Reading or +// refreshing quota does not drain warnings. +// +// RPC method: session.quota.takeWarnings. +// +// Returns: Pending warnings returned and cleared by session.quota.takeWarnings. +func (a *QuotaAPI) TakeWarnings(ctx context.Context) (*SessionQuotaTakeWarningsResult, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request(ctx, "session.quota.takeWarnings", req) + if err != nil { + return nil, err + } + var result SessionQuotaTakeWarningsResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: RemoteAPI contains experimental APIs that may change or be removed. type RemoteAPI sessionAPI @@ -33781,7 +37006,7 @@ func (a *ShellAPI) CancelUserRequested(ctx context.Context, params *ShellCancelU return &result, nil } -// Exec starts a shell command and streams output through session notifications. The command +// Exec starts a shell command, returning an RPC error if it cannot be spawned. The command // runs as the leader of its own process group (POSIX) or in a dedicated job object // (Windows), so a forced termination — via "shell.kill", the request timeout, or session // disposal — signals that whole group/job rather than only the direct child. Two gaps are @@ -33793,10 +37018,10 @@ func (a *ShellAPI) CancelUserRequested(ctx context.Context, params *ShellCancelU // RPC method: session.shell.exec. // // Parameters: Shell command to run, with optional working directory and timeout in -// milliseconds. +// milliseconds. Spawn failures return an RPC error. // -// Returns: Identifier of the spawned process, used to correlate streamed output and exit -// notifications. +// Returns: Identifier of the spawned shell process, usable with shell.kill while the +// process is running. func (a *ShellAPI) Exec(ctx context.Context, params *ShellExecRequest) (*ShellExecResult, error) { req := map[string]any{"sessionId": a.sessionID} if params != nil { @@ -34497,6 +37722,25 @@ func (a *ToolsAPI) InitializeAndValidate(ctx context.Context) (*ToolsInitializeA return &result, nil } +// ListPendingRequests lists the external tool calls of the session and its sub-agents that +// are still waiting for session.tools.handlePendingToolCall. +// +// RPC method: session.tools.listPendingRequests. +// +// Returns: External tool calls still waiting for a result. +func (a *ToolsAPI) ListPendingRequests(ctx context.Context) (*PendingExternalToolRequestList, error) { + req := map[string]any{"sessionId": a.sessionID} + raw, err := a.client.Request(ctx, "session.tools.listPendingRequests", req) + if err != nil { + return nil, err + } + var result PendingExternalToolRequestList + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Set atomically replaces the complete externally implemented tool list supplied by the // calling connection. Built-in, MCP/plugin, extension-discovered, subagent, and tools // supplied by other connections remain unchanged. @@ -35378,6 +38622,9 @@ func (a *WorkspacesAPI) Diff(ctx context.Context, params *WorkspacesDiffRequest) if params.IgnoreWhitespace != nil { req["ignoreWhitespace"] = *params.IgnoreWhitespace } + if params.IncludeContents != nil { + req["includeContents"] = *params.IncludeContents + } req["mode"] = params.Mode } raw, err := a.client.Request(ctx, "session.workspaces.diff", req) @@ -35749,7 +38996,9 @@ type SessionRPC struct { Plan *PlanAPI Plugins *PluginsAPI Provider *ProviderAPI + Providers *ProvidersAPI Queue *QueueAPI + Quota *QuotaAPI Remote *RemoteAPI Sandbox *SandboxAPI Schedule *ScheduleAPI @@ -36086,7 +39335,9 @@ func NewSessionRPC(client *jsonrpc2.Client, sessionID string) *SessionRPC { r.Plan = (*PlanAPI)(&r.common) r.Plugins = (*PluginsAPI)(&r.common) r.Provider = (*ProviderAPI)(&r.common) + r.Providers = (*ProvidersAPI)(&r.common) r.Queue = (*QueueAPI)(&r.common) + r.Quota = (*QuotaAPI)(&r.common) r.Remote = (*RemoteAPI)(&r.common) r.Sandbox = (*SandboxAPI)(&r.common) r.Schedule = (*ScheduleAPI)(&r.common) @@ -36528,6 +39779,35 @@ func (a *InternalMCPAPI) ReloadWithConfig(ctx context.Context, params *MCPReload return &result, nil } +// SetConnectedIdeInfo records the IDE the host is connected to, so the agent's system +// prompt can name it and its workspace folder. Null or an omitted `ide` clears the recorded +// value, which is how a host reports that it is disconnected; there is no separate clear +// method. Both `ideName` and `workspaceFolder` are required together, because half a state +// cannot be attributed to a project. +// +// RPC method: session.mcp.setConnectedIdeInfo. +// +// Parameters: Records which IDE the host is connected to, or clears it. +// Internal: SetConnectedIdeInfo is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalMCPAPI) SetConnectedIdeInfo(ctx context.Context, params *SessionMCPSetConnectedIdeInfoParams) (*SessionMCPSetConnectedIdeInfoResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + if params.Ide != nil { + req["ide"] = *params.Ide + } + } + raw, err := a.client.Request(ctx, "session.mcp.setConnectedIdeInfo", req) + if err != nil { + return nil, err + } + var result SessionMCPSetConnectedIdeInfoResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // UnregisterExternalClient 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. @@ -36603,6 +39883,9 @@ func (a *InternalModelAPI) ApplyStartupOverlay(ctx context.Context, params *Mode if params.RepoModel != nil { req["repoModel"] = *params.RepoModel } + if params.RepoModelProviderID != nil { + req["repoModelProviderId"] = *params.RepoModelProviderID + } if params.RepoReasoningEffort != nil { req["repoReasoningEffort"] = *params.RepoReasoningEffort } @@ -36621,6 +39904,31 @@ func (a *InternalModelAPI) ApplyStartupOverlay(ctx context.Context, params *Mode return &result, nil } +// ClearStartupSeed clears an unchanged startup model/provider seed before default-model +// resolution. Never clears a resumed or user-selected model. +// +// RPC method: session.model.clearStartupSeed. +// Internal: ClearStartupSeed is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalModelAPI) ClearStartupSeed(ctx context.Context, params *SessionModelClearStartupSeedRequest) (*SessionModelClearStartupSeedResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["expectedModel"] = params.ExpectedModel + if params.ExpectedProviderID != nil { + req["expectedProviderId"] = *params.ExpectedProviderID + } + } + raw, err := a.client.Request(ctx, "session.model.clearStartupSeed", req) + if err != nil { + return nil, err + } + var result SessionModelClearStartupSeedResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: InternalQueueAPI contains experimental APIs that may change or be removed. type InternalQueueAPI internalSessionAPI @@ -37060,6 +40368,125 @@ func (a *InternalSettingsAPI) Snapshot(ctx context.Context) (*SessionSettingsSna return &result, nil } +// Experimental: InternalUIAPI contains experimental APIs that may change or be removed. +type InternalUIAPI internalSessionAPI + +// HandleHumanAskUser resolves a pending elicitation request after direct interaction in the +// trusted in-process client. Only an accepted response to the built-in ask_user tool can +// become trusted human evidence. +// +// RPC method: session.ui.handleHumanAskUser. +// +// Parameters: Pending elicitation request ID and the user's response (accept/decline/cancel +// + form values). +// +// Returns: Indicates whether the elicitation response was accepted; false if it was already +// resolved by another client. +// Internal: HandleHumanAskUser is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalUIAPI) HandleHumanAskUser(ctx context.Context, params *UIHandlePendingElicitationRequest) (*UIElicitationResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["requestId"] = params.RequestID + req["result"] = params.Result + } + raw, err := a.client.Request(ctx, "session.ui.handleHumanAskUser", req) + if err != nil { + return nil, err + } + var result UIElicitationResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// HandleHumanExitPlanMode resolves a pending `exit_plan_mode.requested` event after direct +// interaction in the trusted in-process client. +// +// RPC method: session.ui.handleHumanExitPlanMode. +// +// Parameters: Request ID of a pending `exit_plan_mode.requested` event and the user's +// response. +// +// Returns: Indicates whether the pending UI request was resolved by this call. +// Internal: HandleHumanExitPlanMode is part of the SDK's internal handshake/plumbing; +// external callers should not use it. +func (a *InternalUIAPI) HandleHumanExitPlanMode(ctx context.Context, params *UIHandlePendingExitPlanModeRequest) (*UIHandlePendingResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["requestId"] = params.RequestID + req["response"] = params.Response + } + raw, err := a.client.Request(ctx, "session.ui.handleHumanExitPlanMode", req) + if err != nil { + return nil, err + } + var result UIHandlePendingResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// HandleHumanUserInput resolves a pending `user_input.requested` event after direct +// interaction in the trusted in-process client. +// +// RPC method: session.ui.handleHumanUserInput. +// +// Parameters: Request ID of a pending `user_input.requested` event and the user's response. +// +// Returns: Indicates whether the pending UI request was resolved by this call. +// Internal: HandleHumanUserInput is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalUIAPI) HandleHumanUserInput(ctx context.Context, params *UIHandlePendingUserInputRequest) (*UIHandlePendingResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + req["requestId"] = params.RequestID + req["response"] = params.Response + } + raw, err := a.client.Request(ctx, "session.ui.handleHumanUserInput", req) + if err != nil { + return nil, err + } + var result UIHandlePendingResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Experimental: InternalUsageAPI contains experimental APIs that may change or be removed. +type InternalUsageAPI internalSessionAPI + +// SetCodeChanges internal host-only replacement of absolute code-change totals reported by +// a relay host. +// +// RPC method: session.usage.setCodeChanges. +// +// Parameters: Internal absolute code-change totals reported by the owning host. +// Internal: SetCodeChanges is part of the SDK's internal handshake/plumbing; external +// callers should not use it. +func (a *InternalUsageAPI) SetCodeChanges(ctx context.Context, params *SessionUsageSetCodeChangesRequest) (*SessionUsageSetCodeChangesResult, error) { + req := map[string]any{"sessionId": a.sessionID} + if params != nil { + if params.FilesCount != nil { + req["filesCount"] = *params.FilesCount + } + req["linesAdded"] = params.LinesAdded + req["linesRemoved"] = params.LinesRemoved + } + raw, err := a.client.Request(ctx, "session.usage.setCodeChanges", req) + if err != nil { + return nil, err + } + var result SessionUsageSetCodeChangesResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, err + } + return &result, nil +} + // Experimental: InternalWorkflowAPI contains experimental APIs that may change or be // removed. type InternalWorkflowAPI internalSessionAPI @@ -37168,6 +40595,8 @@ type InternalSessionRPC struct { Queue *InternalQueueAPI Schedule *InternalScheduleAPI Settings *InternalSettingsAPI + UI *InternalUIAPI + Usage *InternalUsageAPI Workflow *InternalWorkflowAPI } @@ -37215,6 +40644,8 @@ func NewInternalSessionRPC(client *jsonrpc2.Client, sessionID string) *InternalS r.Queue = (*InternalQueueAPI)(&r.common) r.Schedule = (*InternalScheduleAPI)(&r.common) r.Settings = (*InternalSettingsAPI)(&r.common) + r.UI = (*InternalUIAPI)(&r.common) + r.Usage = (*InternalUsageAPI)(&r.common) r.Workflow = (*InternalWorkflowAPI)(&r.common) return r } @@ -37328,6 +40759,14 @@ type SessionFSHandler interface { // // Returns: File content as a UTF-8 string, or a filesystem error if the read failed. ReadFile(request *SessionFSReadFileRequest) (*SessionFSReadFileResult, error) + // ReadFileBytes reads binary file content from the client-provided session filesystem. + // + // RPC method: sessionFs.readFileBytes. + // + // Parameters: Path of the binary file to read from the client-provided session filesystem. + // + // Returns: File bytes as standard base64, or a filesystem error if the read failed. + ReadFileBytes(request *SessionFSReadFileBytesRequest) (*SessionFSReadFileBytesResult, error) // Renames or moves a path in the client-provided session filesystem. // // RPC method: sessionFs.rename. @@ -37395,6 +40834,15 @@ type SessionFSHandler interface { // // Returns: Describes a filesystem error. WriteFile(request *SessionFSWriteFileRequest) (*SessionFSError, error) + // WriteFileBytes writes binary file content to the client-provided session filesystem. + // + // RPC method: sessionFs.writeFileBytes. + // + // Parameters: File path, standard-base64-encoded bytes to write, and optional mode for the + // client-provided session filesystem. + // + // Returns: Describes a filesystem error. + WriteFileBytes(request *SessionFSWriteFileBytesRequest) (*SessionFSError, error) } // Experimental: TasksHandler contains experimental APIs that may change or be removed. @@ -37644,6 +41092,25 @@ func RegisterClientSessionAPIHandlers(client *jsonrpc2.Client, getHandlers func( } return raw, nil }) + client.SetRequestHandler("sessionFs.readFileBytes", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + var request SessionFSReadFileBytesRequest + if err := json.Unmarshal(params, &request); err != nil { + return nil, &jsonrpc2.Error{Code: -32602, Message: fmt.Sprintf("Invalid params: %v", err)} + } + handlers := getHandlers(request.SessionID) + if handlers == nil || handlers.SessionFS == nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("No sessionFs handler registered for session: %s", request.SessionID)} + } + result, err := handlers.SessionFS.ReadFileBytes(&request) + if err != nil { + return nil, clientSessionHandlerError(err) + } + raw, err := json.Marshal(result) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("Failed to marshal response: %v", err)} + } + return raw, nil + }) client.SetRequestHandler("sessionFs.rename", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { var request SessionFSRenameRequest if err := json.Unmarshal(params, &request); err != nil { @@ -37777,6 +41244,25 @@ func RegisterClientSessionAPIHandlers(client *jsonrpc2.Client, getHandlers func( } return raw, nil }) + client.SetRequestHandler("sessionFs.writeFileBytes", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + var request SessionFSWriteFileBytesRequest + if err := json.Unmarshal(params, &request); err != nil { + return nil, &jsonrpc2.Error{Code: -32602, Message: fmt.Sprintf("Invalid params: %v", err)} + } + handlers := getHandlers(request.SessionID) + if handlers == nil || handlers.SessionFS == nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("No sessionFs handler registered for session: %s", request.SessionID)} + } + result, err := handlers.SessionFS.WriteFileBytes(&request) + if err != nil { + return nil, clientSessionHandlerError(err) + } + raw, err := json.Marshal(result) + if err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: fmt.Sprintf("Failed to marshal response: %v", err)} + } + return raw, nil + }) client.SetRequestHandler("tasks.cancel", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { var request ClientTaskCancelRequest if err := json.Unmarshal(params, &request); err != nil { diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index 320292785a..471e491a87 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -21,6 +21,12 @@ func unmarshalAuthInfo(data []byte) (AuthInfo, error) { } switch raw.Type { + case AuthInfoTypeAccount: + var d AccountAuthInfo + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case AuthInfoTypeAPIKey: var d APIKeyAuthInfo if err := json.Unmarshal(data, &d); err != nil { @@ -85,6 +91,17 @@ func (r RawAuthInfoData) MarshalJSON() ([]byte, error) { }) } +func (r AccountAuthInfo) MarshalJSON() ([]byte, error) { + type alias AccountAuthInfo + return json.Marshal(struct { + Type AuthInfoType `json:"type"` + alias + }{ + Type: r.Type(), + alias: alias(r), + }) +} + func (r APIKeyAuthInfo) MarshalJSON() ([]byte, error) { type alias APIKeyAuthInfo return json.Marshal(struct { @@ -2573,69 +2590,6 @@ func (r UnsupportedEnqueueCommandResult) MarshalJSON() ([]byte, error) { }) } -func unmarshalEntraTokenAcquireResult(data []byte) (EntraTokenAcquireResult, error) { - if string(data) == "null" { - return nil, nil - } - type rawUnion struct { - Status EntraTokenAcquireResultStatus `json:"status"` - } - var raw rawUnion - if err := json.Unmarshal(data, &raw); err != nil { - return nil, err - } - - switch raw.Status { - case EntraTokenAcquireResultStatusInteractionRequired: - var d EntraTokenAcquireResultInteractionRequired - if err := json.Unmarshal(data, &d); err != nil { - return nil, err - } - return &d, nil - case EntraTokenAcquireResultStatusOk: - var d EntraTokenAcquireResultOk - if err := json.Unmarshal(data, &d); err != nil { - return nil, err - } - return &d, nil - default: - return &RawEntraTokenAcquireResultData{Discriminator: raw.Status, Raw: data}, nil - } -} - -func (r RawEntraTokenAcquireResultData) MarshalJSON() ([]byte, error) { - if r.Raw != nil { - return r.Raw, nil - } - return json.Marshal(struct { - Status EntraTokenAcquireResultStatus `json:"status"` - }{ - Status: r.Discriminator, - }) -} - -func (r EntraTokenAcquireResultInteractionRequired) MarshalJSON() ([]byte, error) { - type alias EntraTokenAcquireResultInteractionRequired - return json.Marshal(struct { - Status EntraTokenAcquireResultStatus `json:"status"` - alias - }{ - Status: r.Status(), - alias: alias(r), - }) -} - -func (r EntraTokenAcquireResultOk) MarshalJSON() ([]byte, error) { - type alias EntraTokenAcquireResultOk - return json.Marshal(struct { - Status EntraTokenAcquireResultStatus `json:"status"` - alias - }{ - Status: r.Status(), - alias: alias(r), - }) -} - func (r EventLogTypes) MarshalJSON() ([]byte, error) { if r.String != nil { return json.Marshal(r.String) @@ -3053,6 +3007,58 @@ func (r GitHubTokenAcquireResultToken) MarshalJSON() ([]byte, error) { }) } +func (r InstalledPluginSource) MarshalJSON() ([]byte, error) { + if r.InstalledPluginSourceGitHub != nil { + return json.Marshal(r.InstalledPluginSourceGitHub) + } + if r.InstalledPluginSourceLocal != nil { + return json.Marshal(r.InstalledPluginSourceLocal) + } + if r.InstalledPluginSourceURL != nil { + return json.Marshal(r.InstalledPluginSourceURL) + } + if r.String != nil { + return json.Marshal(r.String) + } + return []byte("null"), nil +} + +func (r *InstalledPluginSource) UnmarshalJSON(data []byte) error { + if string(data) == "null" { + *r = InstalledPluginSource{} + return nil + } + { + var value InstalledPluginSourceGitHub + if err := json.Unmarshal(data, &value); err == nil { + *r = InstalledPluginSource{InstalledPluginSourceGitHub: &value} + return nil + } + } + { + var value InstalledPluginSourceLocal + if err := json.Unmarshal(data, &value); err == nil { + *r = InstalledPluginSource{InstalledPluginSourceLocal: &value} + return nil + } + } + { + var value InstalledPluginSourceURL + if err := json.Unmarshal(data, &value); err == nil { + *r = InstalledPluginSource{InstalledPluginSourceURL: &value} + return nil + } + } + { + var value string + if err := json.Unmarshal(data, &value); err == nil { + *r = InstalledPluginSource{String: &value} + return nil + } + } + return errors.New("data did not match any union variant for InstalledPluginSource") +} + func (r *HandlePendingToolCallRequest) UnmarshalJSON(data []byte) error { type rawHandlePendingToolCallRequest struct { Error *string `json:"error,omitempty"` @@ -3606,58 +3612,6 @@ func (r *InstallationConfirmationRequest) UnmarshalJSON(data []byte) error { return nil } -func (r InstalledPluginSource) MarshalJSON() ([]byte, error) { - if r.InstalledPluginSourceGitHub != nil { - return json.Marshal(r.InstalledPluginSourceGitHub) - } - if r.InstalledPluginSourceLocal != nil { - return json.Marshal(r.InstalledPluginSourceLocal) - } - if r.InstalledPluginSourceURL != nil { - return json.Marshal(r.InstalledPluginSourceURL) - } - if r.String != nil { - return json.Marshal(r.String) - } - return []byte("null"), nil -} - -func (r *InstalledPluginSource) UnmarshalJSON(data []byte) error { - if string(data) == "null" { - *r = InstalledPluginSource{} - return nil - } - { - var value InstalledPluginSourceGitHub - if err := json.Unmarshal(data, &value); err == nil { - *r = InstalledPluginSource{InstalledPluginSourceGitHub: &value} - return nil - } - } - { - var value InstalledPluginSourceLocal - if err := json.Unmarshal(data, &value); err == nil { - *r = InstalledPluginSource{InstalledPluginSourceLocal: &value} - return nil - } - } - { - var value InstalledPluginSourceURL - if err := json.Unmarshal(data, &value); err == nil { - *r = InstalledPluginSource{InstalledPluginSourceURL: &value} - return nil - } - } - { - var value string - if err := json.Unmarshal(data, &value); err == nil { - *r = InstalledPluginSource{String: &value} - return nil - } - } - return errors.New("data did not match any union variant for InstalledPluginSource") -} - func matchesMCPSerializableServerConfigMCPServerConfigHTTP(data []byte) bool { var rawGroup0 struct { Command json.RawMessage `json:"command"` @@ -5557,6 +5511,101 @@ func (r *MCPStartServerRequest) UnmarshalJSON(data []byte) error { return nil } +func unmarshalProtocolMarkerSectionOverride(data []byte) (ProtocolMarkerSectionOverride, error) { + if string(data) == "null" { + return nil, nil + } + type rawUnion struct { + Action ProtocolMarkerSectionOverrideAction `json:"action"` + } + var raw rawUnion + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + switch raw.Action { + case ProtocolMarkerSectionOverrideActionPreserve: + var d ProtocolMarkerSectionOverridePreserve + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case ProtocolMarkerSectionOverrideActionTransform: + var d ProtocolMarkerSectionOverrideTransform + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + default: + return &RawProtocolMarkerSectionOverrideData{Discriminator: raw.Action, Raw: data}, nil + } +} + +func (r RawProtocolMarkerSectionOverrideData) MarshalJSON() ([]byte, error) { + if r.Raw != nil { + return r.Raw, nil + } + return json.Marshal(struct { + Action ProtocolMarkerSectionOverrideAction `json:"action"` + }{ + Action: r.Discriminator, + }) +} + +func (r ProtocolMarkerSectionOverridePreserve) MarshalJSON() ([]byte, error) { + type alias ProtocolMarkerSectionOverridePreserve + return json.Marshal(struct { + Action ProtocolMarkerSectionOverrideAction `json:"action"` + alias + }{ + Action: r.Action(), + alias: alias(r), + }) +} + +func (r ProtocolMarkerSectionOverrideTransform) MarshalJSON() ([]byte, error) { + type alias ProtocolMarkerSectionOverrideTransform + return json.Marshal(struct { + Action ProtocolMarkerSectionOverrideAction `json:"action"` + alias + }{ + Action: r.Action(), + alias: alias(r), + }) +} + +func (r ProtocolSectionOverride) MarshalJSON() ([]byte, error) { + if r.ProtocolMarkerSectionOverride != nil { + return json.Marshal(r.ProtocolMarkerSectionOverride) + } + if r.ProtocolStaticSectionOverride != nil { + return json.Marshal(r.ProtocolStaticSectionOverride) + } + return []byte("null"), nil +} + +func (r *ProtocolSectionOverride) UnmarshalJSON(data []byte) error { + if string(data) == "null" { + *r = ProtocolSectionOverride{} + return nil + } + { + value, err := unmarshalProtocolMarkerSectionOverride(data) + if err == nil { + *r = ProtocolSectionOverride{ProtocolMarkerSectionOverride: value} + return nil + } + } + { + var value ProtocolStaticSectionOverride + if err := json.Unmarshal(data, &value); err == nil { + *r = ProtocolSectionOverride{ProtocolStaticSectionOverride: &value} + return nil + } + } + return errors.New("data did not match any union variant for ProtocolSectionOverride") +} + func unmarshalPermissionDecision(data []byte) (PermissionDecision, error) { if string(data) == "null" { return nil, nil @@ -6825,101 +6874,6 @@ func (r *PermissionLocationAddToolApprovalParams) UnmarshalJSON(data []byte) err return nil } -func unmarshalProtocolMarkerSectionOverride(data []byte) (ProtocolMarkerSectionOverride, error) { - if string(data) == "null" { - return nil, nil - } - type rawUnion struct { - Action ProtocolMarkerSectionOverrideAction `json:"action"` - } - var raw rawUnion - if err := json.Unmarshal(data, &raw); err != nil { - return nil, err - } - - switch raw.Action { - case ProtocolMarkerSectionOverrideActionPreserve: - var d ProtocolMarkerSectionOverridePreserve - if err := json.Unmarshal(data, &d); err != nil { - return nil, err - } - return &d, nil - case ProtocolMarkerSectionOverrideActionTransform: - var d ProtocolMarkerSectionOverrideTransform - if err := json.Unmarshal(data, &d); err != nil { - return nil, err - } - return &d, nil - default: - return &RawProtocolMarkerSectionOverrideData{Discriminator: raw.Action, Raw: data}, nil - } -} - -func (r RawProtocolMarkerSectionOverrideData) MarshalJSON() ([]byte, error) { - if r.Raw != nil { - return r.Raw, nil - } - return json.Marshal(struct { - Action ProtocolMarkerSectionOverrideAction `json:"action"` - }{ - Action: r.Discriminator, - }) -} - -func (r ProtocolMarkerSectionOverridePreserve) MarshalJSON() ([]byte, error) { - type alias ProtocolMarkerSectionOverridePreserve - return json.Marshal(struct { - Action ProtocolMarkerSectionOverrideAction `json:"action"` - alias - }{ - Action: r.Action(), - alias: alias(r), - }) -} - -func (r ProtocolMarkerSectionOverrideTransform) MarshalJSON() ([]byte, error) { - type alias ProtocolMarkerSectionOverrideTransform - return json.Marshal(struct { - Action ProtocolMarkerSectionOverrideAction `json:"action"` - alias - }{ - Action: r.Action(), - alias: alias(r), - }) -} - -func (r ProtocolSectionOverride) MarshalJSON() ([]byte, error) { - if r.ProtocolMarkerSectionOverride != nil { - return json.Marshal(r.ProtocolMarkerSectionOverride) - } - if r.ProtocolStaticSectionOverride != nil { - return json.Marshal(r.ProtocolStaticSectionOverride) - } - return []byte("null"), nil -} - -func (r *ProtocolSectionOverride) UnmarshalJSON(data []byte) error { - if string(data) == "null" { - *r = ProtocolSectionOverride{} - return nil - } - { - value, err := unmarshalProtocolMarkerSectionOverride(data) - if err == nil { - *r = ProtocolSectionOverride{ProtocolMarkerSectionOverride: value} - return nil - } - } - { - var value ProtocolStaticSectionOverride - if err := json.Unmarshal(data, &value); err == nil { - *r = ProtocolSectionOverride{ProtocolStaticSectionOverride: &value} - return nil - } - } - return errors.New("data did not match any union variant for ProtocolSectionOverride") -} - func unmarshalPushAttachment(data []byte) (PushAttachment, error) { if string(data) == "null" { return nil, nil @@ -7771,6 +7725,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { AskUserDisabled *bool `json:"askUserDisabled,omitempty"` AuthClientIDMetadataURL *string `json:"authClientIdMetadataUrl,omitempty"` AuthInfo json.RawMessage `json:"authInfo,omitempty"` + AutoTierIsExplicit *bool `json:"autoTierIsExplicit,omitempty"` AvailableTools []string `json:"availableTools,omitzero"` Capi *CapiSessionOptions `json:"capi,omitempty"` ClientKind *string `json:"clientKind,omitempty"` @@ -7792,6 +7747,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { EnableScriptSafety *bool `json:"enableScriptSafety,omitempty"` EnableSkills *bool `json:"enableSkills,omitempty"` EnableStreaming *bool `json:"enableStreaming,omitempty"` + EnforceManagedModelDefaults *bool `json:"enforceManagedModelDefaults,omitempty"` EnvValueMode *SessionOpenOptionsEnvValueMode `json:"envValueMode,omitempty"` EventsLogDirectory *string `json:"eventsLogDirectory,omitempty"` EventsLogIncludesSubagents *bool `json:"eventsLogIncludesSubagents,omitempty"` @@ -7857,6 +7813,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { } r.AuthInfo = value } + r.AutoTierIsExplicit = raw.AutoTierIsExplicit r.AvailableTools = raw.AvailableTools r.Capi = raw.Capi r.ClientKind = raw.ClientKind @@ -7878,6 +7835,7 @@ func (r *SessionOpenOptions) UnmarshalJSON(data []byte) error { r.EnableScriptSafety = raw.EnableScriptSafety r.EnableSkills = raw.EnableSkills r.EnableStreaming = raw.EnableStreaming + r.EnforceManagedModelDefaults = raw.EnforceManagedModelDefaults r.EnvValueMode = raw.EnvValueMode r.EventsLogDirectory = raw.EventsLogDirectory r.EventsLogIncludesSubagents = raw.EventsLogIncludesSubagents @@ -8203,6 +8161,12 @@ func unmarshalSettableAuthInfo(data []byte) (SettableAuthInfo, error) { } switch raw.Type { + case SettableAuthInfoTypeAccount: + var d AccountAuthInfo + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil case SettableAuthInfoTypeAPIKey: var d APIKeyAuthInfo if err := json.Unmarshal(data, &d); err != nil { diff --git a/go/rpc/zsession_encoding.go b/go/rpc/zsession_encoding.go index ae79439771..2db5b44582 100644 --- a/go/rpc/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -251,6 +251,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeHumanResponseRecorded: + var d HumanResponseRecordedData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeMCPAppToolCallComplete: var d MCPAppToolCallCompleteData if err := json.Unmarshal(raw.Data, &d); err != nil { @@ -593,6 +599,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeSessionManagedPluginProgress: + var d SessionManagedPluginProgressData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeSessionManagedSettingsEnforced: var d SessionManagedSettingsEnforcedData if err := json.Unmarshal(raw.Data, &d); err != nil { @@ -671,6 +683,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeSessionQuotaObservation: + var d SessionQuotaObservationData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeSessionRemoteSteerableChanged: var d SessionRemoteSteerableChangedData if err := json.Unmarshal(raw.Data, &d); err != nil { @@ -887,6 +905,12 @@ func (e *SessionEvent) UnmarshalJSON(data []byte) error { return err } e.Data = &d + case SessionEventTypeToolShellOutput: + var d ToolShellOutputData + if err := json.Unmarshal(raw.Data, &d); err != nil { + return err + } + e.Data = &d case SessionEventTypeToolUserRequested: var d ToolUserRequestedData if err := json.Unmarshal(raw.Data, &d); err != nil { @@ -2643,6 +2667,110 @@ func (r *PermissionCompletedData) UnmarshalJSON(data []byte) error { return nil } +func unmarshalHumanResponseRecordedResponse(data []byte) (HumanResponseRecordedResponse, error) { + if string(data) == "null" { + return nil, nil + } + type rawUnion struct { + ResponseKind HumanResponseRecordedResponseResponseKind `json:"responseKind"` + } + var raw rawUnion + if err := json.Unmarshal(data, &raw); err != nil { + return nil, err + } + + switch raw.ResponseKind { + case HumanResponseRecordedResponseResponseKindAskUser: + var d HumanResponseRecordedResponseAskUser + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case HumanResponseRecordedResponseResponseKindExitPlanMode: + var d HumanResponseRecordedResponseExitPlanMode + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + case HumanResponseRecordedResponseResponseKindUserInput: + var d HumanResponseRecordedResponseUserInput + if err := json.Unmarshal(data, &d); err != nil { + return nil, err + } + return &d, nil + default: + return &RawHumanResponseRecordedResponse{Discriminator: raw.ResponseKind, Raw: data}, nil + } +} + +func (r RawHumanResponseRecordedResponse) MarshalJSON() ([]byte, error) { + if r.Raw != nil { + return r.Raw, nil + } + return json.Marshal(struct { + ResponseKind HumanResponseRecordedResponseResponseKind `json:"responseKind"` + }{ + ResponseKind: r.Discriminator, + }) +} + +func (r HumanResponseRecordedResponseAskUser) MarshalJSON() ([]byte, error) { + type alias HumanResponseRecordedResponseAskUser + return json.Marshal(struct { + ResponseKind HumanResponseRecordedResponseResponseKind `json:"responseKind"` + alias + }{ + ResponseKind: r.ResponseKind(), + alias: alias(r), + }) +} + +func (r HumanResponseRecordedResponseExitPlanMode) MarshalJSON() ([]byte, error) { + type alias HumanResponseRecordedResponseExitPlanMode + return json.Marshal(struct { + ResponseKind HumanResponseRecordedResponseResponseKind `json:"responseKind"` + alias + }{ + ResponseKind: r.ResponseKind(), + alias: alias(r), + }) +} + +func (r HumanResponseRecordedResponseUserInput) MarshalJSON() ([]byte, error) { + type alias HumanResponseRecordedResponseUserInput + return json.Marshal(struct { + ResponseKind HumanResponseRecordedResponseResponseKind `json:"responseKind"` + alias + }{ + ResponseKind: r.ResponseKind(), + alias: alias(r), + }) +} + +func (r *HumanResponseRecordedData) UnmarshalJSON(data []byte) error { + type rawHumanResponseRecordedData struct { + Actor HumanResponseActor `json:"actor"` + RequestID string `json:"requestId"` + Response json.RawMessage `json:"response"` + ToolCallID *string `json:"toolCallId,omitempty"` + } + var raw rawHumanResponseRecordedData + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + r.Actor = raw.Actor + r.RequestID = raw.RequestID + if raw.Response != nil { + value, err := unmarshalHumanResponseRecordedResponse(raw.Response) + if err != nil { + return err + } + r.Response = value + } + r.ToolCallID = raw.ToolCallID + return nil +} + func (r *SessionExtensionsAttachmentsPushedData) UnmarshalJSON(data []byte) error { type rawSessionExtensionsAttachmentsPushedData struct { Attachments []json.RawMessage `json:"attachments"` diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 1c8c416632..a9bc3e7a63 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -97,6 +97,7 @@ const ( SessionEventTypeHookEnd SessionEventType = "hook.end" SessionEventTypeHookProgress SessionEventType = "hook.progress" SessionEventTypeHookStart SessionEventType = "hook.start" + SessionEventTypeHumanResponseRecorded SessionEventType = "human_response.recorded" SessionEventTypeMCPAppToolCallComplete SessionEventType = "mcp_app.tool_call_complete" SessionEventTypeMCPHeadersRefreshCompleted SessionEventType = "mcp.headers_refresh_completed" SessionEventTypeMCPHeadersRefreshRequired SessionEventType = "mcp.headers_refresh_required" @@ -194,6 +195,9 @@ const ( SessionEventTypeSessionInfo SessionEventType = "session.info" SessionEventTypeSessionLimitsExhaustedCompleted SessionEventType = "session_limits_exhausted.completed" SessionEventTypeSessionLimitsExhaustedRequested SessionEventType = "session_limits_exhausted.requested" + // Experimental: SessionEventTypeSessionManagedPluginProgress identifies an experimental + // event that may change or be removed. + SessionEventTypeSessionManagedPluginProgress SessionEventType = "session.managed_plugin_progress" // Experimental: SessionEventTypeSessionManagedSettingsEnforced identifies an experimental // event that may change or be removed. SessionEventTypeSessionManagedSettingsEnforced SessionEventType = "session.managed_settings_enforced" @@ -211,8 +215,11 @@ const ( SessionEventTypeSessionPermissionRecovery SessionEventType = "session.permission_recovery" // Experimental: SessionEventTypeSessionPermissionsChanged identifies an experimental event // that may change or be removed. - SessionEventTypeSessionPermissionsChanged SessionEventType = "session.permissions_changed" - SessionEventTypeSessionPlanChanged SessionEventType = "session.plan_changed" + SessionEventTypeSessionPermissionsChanged SessionEventType = "session.permissions_changed" + SessionEventTypeSessionPlanChanged SessionEventType = "session.plan_changed" + // Experimental: SessionEventTypeSessionQuotaObservation identifies an experimental event + // that may change or be removed. + SessionEventTypeSessionQuotaObservation SessionEventType = "session.quota_observation" SessionEventTypeSessionRemoteSteerableChanged SessionEventType = "session.remote_steerable_changed" SessionEventTypeSessionResume SessionEventType = "session.resume" SessionEventTypeSessionScheduleCancelled SessionEventType = "session.schedule_cancelled" @@ -241,20 +248,22 @@ const ( SessionEventTypeSkillInvoked SessionEventType = "skill.invoked" // Experimental: SessionEventTypeSkillInvokedRef identifies an experimental event that may // change or be removed. - SessionEventTypeSkillInvokedRef SessionEventType = "skill.invoked_ref" - SessionEventTypeSubagentCompleted SessionEventType = "subagent.completed" - SessionEventTypeSubagentConfigured SessionEventType = "subagent.configured" - SessionEventTypeSubagentDeselected SessionEventType = "subagent.deselected" - SessionEventTypeSubagentFailed SessionEventType = "subagent.failed" - SessionEventTypeSubagentSelected SessionEventType = "subagent.selected" - SessionEventTypeSubagentStarted SessionEventType = "subagent.started" - SessionEventTypeSystemMessage SessionEventType = "system.message" - SessionEventTypeSystemNotification SessionEventType = "system.notification" - SessionEventTypeToolExecutionComplete SessionEventType = "tool.execution_complete" + SessionEventTypeSkillInvokedRef SessionEventType = "skill.invoked_ref" + SessionEventTypeSubagentCompleted SessionEventType = "subagent.completed" + SessionEventTypeSubagentConfigured SessionEventType = "subagent.configured" + SessionEventTypeSubagentDeselected SessionEventType = "subagent.deselected" + SessionEventTypeSubagentFailed SessionEventType = "subagent.failed" + SessionEventTypeSubagentSelected SessionEventType = "subagent.selected" + SessionEventTypeSubagentStarted SessionEventType = "subagent.started" + SessionEventTypeSystemMessage SessionEventType = "system.message" + SessionEventTypeSystemNotification SessionEventType = "system.notification" + SessionEventTypeToolExecutionComplete SessionEventType = "tool.execution_complete" + // Deprecated: SessionEventTypeToolExecutionPartialResult identifies a deprecated event. SessionEventTypeToolExecutionPartialResult SessionEventType = "tool.execution_partial_result" SessionEventTypeToolExecutionProgress SessionEventType = "tool.execution_progress" SessionEventTypeToolExecutionStart SessionEventType = "tool.execution_start" SessionEventTypeToolSearchActivated SessionEventType = "tool_search.activated" + SessionEventTypeToolShellOutput SessionEventType = "tool.shell_output" SessionEventTypeToolUserRequested SessionEventType = "tool.user_requested" // Experimental: SessionEventTypeUIEphemeralQuery identifies an experimental event that may // change or be removed. @@ -326,6 +335,9 @@ type PromptCacheBreakData struct { // Telemetry-safe names of tools whose definition changed since the prior call // Internal: ToolsRedefined is part of the SDK's internal API surface and is not intended for external use. ToolsRedefined []string `json:"toolsRedefined,omitzero"` + // Changed definition parts of redefined tools, as `tool:part` entries; property-level parts only for telemetry-safe tools, whose other names are hashed + // Internal: ToolsRedefinedParts is part of the SDK's internal API surface and is not intended for external use. + ToolsRedefinedParts []string `json:"toolsRedefinedParts,omitzero"` // Raw names of tools redefined since the prior call, restricted because a tool name can be user-authored // Internal: ToolsRedefinedRaw is part of the SDK's internal API surface and is not intended for external use. ToolsRedefinedRaw []string `json:"toolsRedefinedRaw,omitzero"` @@ -343,6 +355,18 @@ type PromptCacheBreakData struct { func (*PromptCacheBreakData) sessionEventData() {} func (*PromptCacheBreakData) Type() SessionEventType { return SessionEventTypePromptCacheBreak } +// A provider-owned quota observation, distinct from per-call usage and charge accounting. +// Experimental: SessionQuotaObservationData is part of an experimental API and may change or be removed. +type SessionQuotaObservationData struct { + // The admitted provider's state observation. Admission observations never contain quantities or reset/percentage semantics. + Observation ProviderQuotaState `json:"observation"` +} + +func (*SessionQuotaObservationData) sessionEventData() {} +func (*SessionQuotaObservationData) Type() SessionEventType { + return SessionEventTypeSessionQuotaObservation +} + // A transient Auto preference failure emitted when the runtime cannot mint or accept a usable model and token pair. The previously effective preference remains active, so SDK clients can surface a non-blocking failure without changing their committed-tier state. This event is ephemeral and is not persisted or replayed on resume. type SessionAutoTierSwitchFailedData struct { // Auto preference that remains effective after the failed request. @@ -426,6 +450,8 @@ type AssistantMessageData struct { ParentToolCallID *string `json:"parentToolCallId,omitempty"` // Generation phase for phased-output models (e.g., thinking vs. response phases) Phase *string `json:"phase,omitempty"` + // Opaque origin provider identity captured at dispatch. Only an exact match with the target provider permits replay of reasoningOpaque, encryptedContent, reasoningBlocks, serverTools, and provider response/item identifiers. Matching model ids or wire protocols is insufficient. Omitted for legacy or unattributed messages; the runtime excludes unproven opaque state when projecting to a known provider. Text content and client tool requests/results remain portable semantic history. + ProviderID *string `json:"providerId,omitempty"` // Neutral provider-tagged reasoning content blocks preserved verbatim for round-tripping. `reasoningText` and `reasoningOpaque` are a lossy derived view of these blocks, retained for display. ReasoningBlocks *AssistantMessageReasoningBlocks `json:"reasoningBlocks,omitempty"` // Opaque/encrypted extended thinking data from Anthropic models. Session-bound and stripped on resume. @@ -503,6 +529,8 @@ type SessionAutoModeResolvedData struct { RouterLatencyMs *float64 `json:"routerLatencyMs,omitempty"` // The routing method the server applied, when Auto Intent ran RoutingMethod *string `json:"routingMethod,omitempty"` + // Short human-readable sentence from the routing service explaining why this model was chosen, for display alongside the model. Present only when the service supplied one: it is omitted for on-device selections, when the service did not provide an explanation, and when a replayed decision made no routing call. The text is display-only and drawn from a fixed catalogue; several distinct routing categories share identical wording, so it cannot be used to recover the category or keyed on programmatically. + SelectionReason *string `json:"selectionReason,omitempty"` // Whether a sticky model choice overrode the router result StickyOverride *bool `json:"stickyOverride,omitempty"` } @@ -754,6 +782,21 @@ type SubagentSelectedData struct { func (*SubagentSelectedData) sessionEventData() {} func (*SubagentSelectedData) Type() SessionEventType { return SessionEventTypeSubagentSelected } +// Deprecated merged replacement snapshot of shell output. Use tool.shell_output for append-only, stream-tagged output instead. +// +// Deprecated: ToolExecutionPartialResultData is deprecated. +type ToolExecutionPartialResultData struct { + // Merged replacement snapshot from the running shell, not an append-only chunk + PartialOutput string `json:"partialOutput"` + // Tool call ID this partial result belongs to + ToolCallID string `json:"toolCallId"` +} + +func (*ToolExecutionPartialResultData) sessionEventData() {} +func (*ToolExecutionPartialResultData) Type() SessionEventType { + return SessionEventTypeToolExecutionPartialResult +} + // Durable record that a canvas instance is open, used to restore open canvases on cold session resume. Intentionally omits the transient url and availability. // Experimental: SessionCanvasRecordedData is part of an experimental API and may change or be removed. type SessionCanvasRecordedData struct { @@ -788,19 +831,43 @@ type SessionCanvasRemovedData struct { func (*SessionCanvasRemovedData) sessionEventData() {} func (*SessionCanvasRemovedData) Type() SessionEventType { return SessionEventTypeSessionCanvasRemoved } +// Durable request-correlated evidence for a typed response to a runtime-owned question or plan review. +type HumanResponseRecordedData struct { + // Controlled actor provenance established at response ingress. + Actor HumanResponseActor `json:"actor"` + // Request ID of the runtime-owned question or plan review. + RequestID string `json:"requestId"` + // Typed request and response payload. + Response HumanResponseRecordedResponse `json:"response"` + // Tool call ID that opened the request, when present. + ToolCallID *string `json:"toolCallId,omitempty"` +} + +func (*HumanResponseRecordedData) sessionEventData() {} +func (*HumanResponseRecordedData) Type() SessionEventType { + return SessionEventTypeHumanResponseRecorded +} + // Durable session usage checkpoint for reconstructing aggregate accounting on resume type SessionUsageCheckpointData struct { + // Complete accounting state captured atomically with the receipt watermarks. + // Internal: AccountingSnapshot is part of the SDK's internal API surface and is not intended for external use. + AccountingSnapshot *UsageGetMetricsResult `json:"accountingSnapshot,omitempty"` // Internal per-model prompt-cache state used to restore expiration tracking on resume // Internal: ModelCacheState is part of the SDK's internal API surface and is not intended for external use. ModelCacheState []UsageCheckpointModelCacheState `json:"modelCacheState,omitzero"` // Internal per-conversation prompt-cache-break detector baselines restored on resume // Internal: PromptCacheBreakState is part of the SDK's internal API surface and is not intended for external use. PromptCacheBreakState []any `json:"promptCacheBreakState,omitzero"` + // Provider/model accounting snapshot that subsumes preceding usage receipts. + ProviderModelMetrics []UsageMetricsProviderModelMetric `json:"providerModelMetrics,omitzero"` // Session-wide accumulated nano-AI units cost at checkpoint time TotalNanoAiu float64 `json:"totalNanoAiu"` // Total number of premium API requests used at checkpoint time // Internal: TotalPremiumRequests is part of the SDK's internal API surface and is not intended for external use. TotalPremiumRequests *float64 `json:"totalPremiumRequests,omitempty"` + // Highest accounted sequence per source session; no per-call deduplication history is retained. + UsageAccountingWatermarks map[string]int64 `json:"usageAccountingWatermarks,omitzero"` } func (*SessionUsageCheckpointData) sessionEventData() {} @@ -1286,6 +1353,20 @@ func (*AssistantFusionPhaseStartedData) Type() SessionEventType { return SessionEventTypeAssistantFusionPhaseStarted } +// Experimental transient presentation-neutral progress for organization-required plugin preparation. Clients should localize the phase copy and display plugin specs without translating them. +// Experimental: SessionManagedPluginProgressData is part of an experimental API and may change or be removed. +type SessionManagedPluginProgressData struct { + // Current preparation phase. + Phase ManagedPluginProgressPhase `json:"phase"` + // Ordered plugin install specs affected by this phase. Empty while managed settings initialize and when preparation completes. + PluginSpecs []string `json:"pluginSpecs"` +} + +func (*SessionManagedPluginProgressData) sessionEventData() {} +func (*SessionManagedPluginProgressData) Type() SessionEventType { + return SessionEventTypeSessionManagedPluginProgress +} + // Experimental transient signal that HydraFusion routing has started for an eligible turn. // Experimental: SessionFusionRouteStartedData is part of an experimental API and may change or be removed. type SessionFusionRouteStartedData struct { @@ -1350,6 +1431,8 @@ type ModelCallFailureData struct { APIEndpoint *AssistantUsageAPIEndpoint `json:"apiEndpoint,omitempty"` // For HTTP 400 failures only: whether the response carried a structured CAPI error envelope (structured_error, a deterministic validation failure) or no error body (bodyless, the transient gateway/proxy signature). Absent for non-400 failures. BadRequestKind *ModelCallFailureBadRequestKind `json:"badRequestKind,omitempty"` + // Where the bring-your-own-key model for the failed call runs and who manages it: "local_managed" (on the device, managed by Copilot), "local_user" (on the device, managed by the user), or "remote_user" (off the device, managed by the user). Absent for Copilot-served models. + ByokKind *string `json:"byokKind,omitempty"` // Duration of the failed API call in milliseconds DurationMs *int64 `json:"durationMs,omitempty"` // For HTTP 400 failures only: the `code` from the CAPI error envelope (e.g. 'model_max_prompt_tokens_exceeded') identifying which deterministic validation failure occurred. Raw server-controlled string, emitted only through restricted telemetry. Absent for bodyless or non-400 failures. @@ -1377,6 +1460,8 @@ type ModelCallFailureData struct { MaxPromptTokens *int64 `json:"maxPromptTokens,omitempty"` // Model identifier used for the failed API call Model *string `json:"model,omitempty"` + // Fixed-set provider family serving the bring-your-own-key model for the failed call (for example "openai", "anthropic", "azure_openai", "ollama", "llama_cpp", or "other"). Never the caller-supplied provider name. Absent for Copilot-served models. + ModelProvider *string `json:"modelProvider,omitempty"` // Parent task tool call ID when this failed model call belongs to a sub-agent ParentToolCallID *string `json:"parentToolCallId,omitempty"` // GitHub request tracing ID (x-github-request-id header) for server-side log correlation @@ -1386,8 +1471,12 @@ type ModelCallFailureData struct { QuotaSnapshots map[string]AssistantUsageQuotaSnapshot `json:"quotaSnapshots,omitzero"` // Reasoning effort level used for the failed model call, if applicable ReasoningEffort *string `json:"reasoningEffort,omitempty"` + // Serialized (uncompressed) byte length of the failed request body. A content-free size signal. + RequestBodyBytes *int64 `json:"requestBodyBytes,omitempty"` // Content-free structural summary of the failing request. Contains only counts and shape flags (no prompt content), so it is safe for unrestricted telemetry. Populated only for client-error (4xx) failures. RequestFingerprint *ModelCallFailureRequestFingerprint `json:"requestFingerprint,omitempty"` + // Zero-based orchestrator retry index of the failed attempt + RetryAttempt *int64 `json:"retryAttempt,omitempty"` // Per-request treatment/eligibility signal returned by the Copilot API in the `X-GitHub-Copilot-Request-TE` response header for the associated model call; `false` when the header was absent or unparseable. Rte *bool `json:"rte,omitempty"` // Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation @@ -1398,6 +1487,10 @@ type ModelCallFailureData struct { StatusCode *int32 `json:"statusCode,omitempty"` // Transport used for the failed model call (http or websocket) Transport *ModelCallFailureTransport `json:"transport,omitempty"` + // Milliseconds spent on the WebSocket attempt before falling back to HTTP + WebsocketFallbackAfterMs *int64 `json:"websocketFallbackAfterMs,omitempty"` + // Why the failed call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred + WebsocketFallbackReason *ModelCallWebSocketFallbackReason `json:"websocketFallbackReason,omitempty"` } func (*ModelCallFailureData) sessionEventData() {} @@ -1672,6 +1765,10 @@ func (*ModelCallFinalResultData) Type() SessionEventType { return SessionEventTy type AssistantUsageData struct { // Number of accepted speculative prediction tokens AcceptedPredictionTokens *int64 `json:"acceptedPredictionTokens,omitempty"` + // Runtime-owned identity shared with the durable accounting receipt. + Accounting *UsageAccountingIdentity `json:"accounting,omitempty"` + // Availability of this call's reported AI-credit amount. Missing billing is unavailable, not a zero charge. + AiCreditsStatus *AiCreditsStatus `json:"aiCreditsStatus,omitempty"` // Completion ID from the model provider (e.g., chatcmpl-abc123) APICallID *string `json:"apiCallId,omitempty"` // API endpoint used for this model call, matching CAPI supported_endpoints vocabulary @@ -1679,6 +1776,8 @@ type AssistantUsageData struct { // Number of tools available to the model for this call // Internal: AvailableToolCount is part of the SDK's internal API surface and is not intended for external use. AvailableToolCount *int64 `json:"availableToolCount,omitempty"` + // Where the bring-your-own-key model runs and who manages it: "local_managed" (on the device, managed by Copilot), "local_user" (on the device, managed by the user), or "remote_user" (off the device, managed by the user). Absent for Copilot-served models. + ByokKind *string `json:"byokKind,omitempty"` // Whether the provider reported prompt-cache usage details for this call // Internal: CacheDetailsReported is part of the SDK's internal API surface and is not intended for external use. CacheDetailsReported *bool `json:"cacheDetailsReported,omitempty"` @@ -1726,6 +1825,10 @@ type AssistantUsageData struct { MaxPromptTokens *int64 `json:"maxPromptTokens,omitempty"` // Model identifier used for this API call Model string `json:"model"` + // Model display name captured for this call, not derived from the current selection. + ModelDisplayName *string `json:"modelDisplayName,omitempty"` + // Fixed-set provider family serving the bring-your-own-key model (for example "openai", "anthropic", "azure_openai", "ollama", "llama_cpp", or "other"). Never the caller-supplied provider name. Absent for Copilot-served models. + ModelProvider *string `json:"modelProvider,omitempty"` // Number of tool calls returned by the model // Internal: NumToolCalls is part of the SDK's internal API surface and is not intended for external use. NumToolCalls *int64 `json:"numToolCalls,omitempty"` @@ -1736,6 +1839,8 @@ type AssistantUsageData struct { // Parent tool call ID when this usage originates from a sub-agent // Deprecated: ParentToolCallID is deprecated. ParentToolCallID *string `json:"parentToolCallId,omitempty"` + // Provider identity and product label captured by the actual call's dispatch path. + Provider *ModelProviderRef `json:"provider,omitempty"` // GitHub request tracing ID (x-github-request-id header) for server-side log correlation ProviderCallID *string `json:"providerCallId,omitempty"` // Per-quota resource usage snapshots, keyed by quota identifier @@ -1749,6 +1854,8 @@ type AssistantUsageData struct { ReasoningTokens *int64 `json:"reasoningTokens,omitempty"` // Number of rejected speculative prediction tokens RejectedPredictionTokens *int64 `json:"rejectedPredictionTokens,omitempty"` + // Serialized (uncompressed) byte length of the request body. A content-free size signal. + RequestBodyBytes *int64 `json:"requestBodyBytes,omitempty"` // Per-request treatment/eligibility signal returned by the Copilot API in the `X-GitHub-Copilot-Request-TE` response header for the associated model call; `false` when the header was absent or unparseable. Rte *bool `json:"rte,omitempty"` // Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation @@ -1769,6 +1876,10 @@ type AssistantUsageData struct { ToolTokenCount *int64 `json:"toolTokenCount,omitempty"` // Transport used for this model call (http or websocket) Transport *AssistantUsageTransport `json:"transport,omitempty"` + // Milliseconds spent on the WebSocket attempt before falling back to HTTP + WebsocketFallbackAfterMs *int64 `json:"websocketFallbackAfterMs,omitempty"` + // Why the call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred + WebsocketFallbackReason *ModelCallWebSocketFallbackReason `json:"websocketFallbackReason,omitempty"` } func (*AssistantUsageData) sessionEventData() {} @@ -1789,6 +1900,21 @@ func (*AssistantServerToolProgressData) Type() SessionEventType { return SessionEventTypeAssistantServerToolProgress } +// Live, append-only shell output. Not persisted or replayed to late subscribers. Text is decoded and redacted per chunk; chunks need not contain complete lines. +type ToolShellOutputData struct { + // Zero-based publication sequence across all output streams for this tool call. Not a byte offset or an OS write-order guarantee. + Sequence int64 `json:"sequence"` + // Output source. Omission means stdout. Terminal output has no separate stdout/stderr attribution. + Stream *ToolShellOutputStream `json:"stream,omitempty"` + // New output to append, without synthetic shell-result markers or stream-switch separators + Text string `json:"text"` + // Tool call ID that owns this shell output + ToolCallID string `json:"toolCallId"` +} + +func (*ToolShellOutputData) sessionEventData() {} +func (*ToolShellOutputData) Type() SessionEventType { return SessionEventTypeToolShellOutput } + // Live-only Auto preference recommendation from Copilot API after a successful Auto model call. // Experimental: SessionAutoTierRecommendationData is part of an experimental API and may change or be removed. type SessionAutoTierRecommendationData struct { @@ -1919,24 +2045,36 @@ func (*ModelCallStartData) Type() SessionEventType { return SessionEventTypeMode type SessionModelChangeData struct { // Committed Auto preference after the model configuration change, when applicable. AutoTier *AutoTier `json:"autoTier,omitempty"` + // True when autoTier is a managed-policy default. Omitted for user-authored and legacy values. + AutoTierManaged *bool `json:"autoTierManaged,omitempty"` // Reason the change happened, when not user-initiated. `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path, or `"refusal_fallback"` when the active model declined a request (content refusal) and the runtime switched to the configured refusal-fallback model. UI clients can use this to render contextual copy. Cause *string `json:"cause,omitempty"` // Context tier after the model change; null explicitly clears a previously selected tier ContextTier *ContextTier `json:"contextTier,omitempty"` + // True when contextTier is a managed-policy default. Omitted for user-authored and legacy values. + ContextTierManaged *bool `json:"contextTierManaged,omitempty"` // Newly selected model identifier NewModel string `json:"newModel"` // Previously committed Auto preference, when one was explicitly selected. PreviousAutoTier *AutoTier `json:"previousAutoTier,omitempty"` // Model that was previously selected, if any PreviousModel *string `json:"previousModel,omitempty"` + // Provider of previousModel, when known. A provider-only change is a model selection change even when the model identifiers are equal. + PreviousProviderID *string `json:"previousProviderId,omitempty"` // Reasoning effort level before the model change, if applicable PreviousReasoningEffort *string `json:"previousReasoningEffort,omitempty"` // Reasoning summary mode before the model change, if applicable PreviousReasoningSummary *ReasoningSummary `json:"previousReasoningSummary,omitempty"` // Output verbosity level before the model change, if applicable PreviousVerbosity *Verbosity `json:"previousVerbosity,omitempty"` + // Provider selected for newModel. Omitted for legacy or unattributed selections; never inferred from a later selection. + ProviderID *string `json:"providerId,omitempty"` // Reasoning effort level after the model change, if applicable ReasoningEffort *string `json:"reasoningEffort,omitempty"` + // True when the reasoning effort is a managed-policy default bound to reasoningEffortModel. Omitted for agent-authored, user-authored, independent, and legacy effort. + ReasoningEffortManaged *bool `json:"reasoningEffortManaged,omitempty"` + // Model that owns effort embedded in an authored model selection. Omitted for independent reasoning-effort overrides and legacy events. + ReasoningEffortModel *string `json:"reasoningEffortModel,omitempty"` // Reasoning summary mode after the model change, if applicable ReasoningSummary *ReasoningSummary `json:"reasoningSummary,omitempty"` // Origin of the effective model change, when known. @@ -2458,18 +2596,18 @@ type SubagentConfiguredData struct { func (*SubagentConfiguredData) sessionEventData() {} func (*SubagentConfiguredData) Type() SessionEventType { return SessionEventTypeSubagentConfigured } -// Runtime enforcement of enterprise managed settings: fires when the session blocks or caps a runtime action because enterprise policy governs it, so SDK clients can explain *why* an action was governed. Unlike `session.managed_settings_resolved` (which reports *what* is managed), this reports a concrete governed action — e.g. a user or host tried to turn on a bypass-permissions escalation while policy disables it. Emitted live (not persisted to the session event log) on user/host-initiated attempts only, never for silent policy application. Marked experimental while the managed-settings surface stabilizes. +// Runtime enforcement of enterprise managed settings: fires when the session blocks or caps a runtime action because enterprise policy governs it, so SDK clients can explain *why* an action was governed. Unlike `session.managed_settings_resolved` (which reports *what* is managed), this reports a concrete governed action — e.g. a user or host tried to turn on bypass permissions or Assisted Permissions while the corresponding policy disables it. Emitted live (not persisted to the session event log) on user/host-initiated attempts only, never for silent policy application. Marked experimental while the managed-settings surface stabilizes. // Experimental: SessionManagedSettingsEnforcedData is part of an experimental API and may change or be removed. type SessionManagedSettingsEnforcedData struct { // The category of runtime action that managed policy governed. Action ManagedSettingsEnforcedAction `json:"action"` - // For a `bypass_permissions_blocked` action, which permission-escalation primitive was refused. Absent for actions without a specific escalation primitive. + // For a `bypass_permissions_blocked` action, which permission-mode or escalation primitive was refused. Absent for actions without a specific primitive. Escalation *ManagedSettingsEnforcedEscalation `json:"escalation,omitempty"` // Whether the enforcement was forced by fail-closed handling (managed policy could not be determined) rather than an explicit managed setting. When true, `setting` still names the restriction that was applied. FailClosed bool `json:"failClosed"` // A human-readable explanation of why the action was governed, suitable for surfacing to the user. Message string `json:"message"` - // The managed setting key responsible for the enforcement (e.g. `permissions.disableBypassPermissionsMode`). + // The managed setting key responsible for the enforcement (for example `permissions.disableBypassPermissionsMode` or `permissions.disableAssistedPermissionsMode`). Setting string `json:"setting"` } @@ -2598,10 +2736,14 @@ type SessionStartData struct { AlreadyInUse *bool `json:"alreadyInUse,omitempty"` // Auto routing preference selected at session creation time AutoTier *AutoTier `json:"autoTier,omitempty"` + // True when autoTier is a managed-policy default. Omitted for user-authored and legacy values. + AutoTierManaged *bool `json:"autoTierManaged,omitempty"` // Working directory and git context at session start Context *WorkingDirectoryContext `json:"context,omitempty"` // Context tier selected at session creation time for models with tiered context pricing; null when no tier is selected (e.g., non-tiered model) ContextTier *ContextTier `json:"contextTier,omitempty"` + // True when contextTier is a managed-policy default. Omitted for user-authored and legacy values. + ContextTierManaged *bool `json:"contextTierManaged,omitempty"` // Version string of the Copilot application CopilotVersion string `json:"copilotVersion"` // When set, identifies a parent session whose context this session continues — e.g., a detached headless rem-agent run launched on the parent's interactive shutdown. Telemetry from this session is reported under the parent's session_id. @@ -2610,8 +2752,14 @@ type SessionStartData struct { GitHubMCPToolConfig *GitHubMCPToolConfig `json:"githubMcpToolConfig,omitempty"` // Identifier of the software producing the events (e.g., "copilot-agent") Producer string `json:"producer"` + // Provider of selectedModel at creation time, when explicitly selected. + ProviderID *string `json:"providerId,omitempty"` // Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") ReasoningEffort *string `json:"reasoningEffort,omitempty"` + // True when the reasoning effort is a managed-policy default bound to reasoningEffortModel. Omitted for agent-authored, user-authored, independent, and legacy effort. + ReasoningEffortManaged *bool `json:"reasoningEffortManaged,omitempty"` + // Model that owns effort embedded in an authored model selection. Omitted for independent reasoning-effort overrides and legacy events. + ReasoningEffortModel *string `json:"reasoningEffortModel,omitempty"` // Reasoning summary mode used for model calls, if applicable (e.g. "none", "concise", "detailed") ReasoningSummary *ReasoningSummary `json:"reasoningSummary,omitempty"` // Whether this session supports remote steering via GitHub @@ -2678,18 +2826,28 @@ type SessionResumeData struct { AlreadyInUse *bool `json:"alreadyInUse,omitempty"` // Auto routing preference active at resume time AutoTier *AutoTier `json:"autoTier,omitempty"` + // True when autoTier is a managed-policy default. Omitted for user-authored and legacy values. + AutoTierManaged *bool `json:"autoTierManaged,omitempty"` // Updated working directory and git context at resume time Context *WorkingDirectoryContext `json:"context,omitempty"` // Context tier currently selected at resume time; null when no tier is active ContextTier *ContextTier `json:"contextTier,omitempty"` + // True when contextTier is a managed-policy default. Omitted for user-authored and legacy values. + ContextTierManaged *bool `json:"contextTierManaged,omitempty"` // When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false or omitted, pending work is normally marked as interrupted unless the resume passively joined live work owned by another client; sessionWasActive distinguishes that case. ContinuePendingWork *bool `json:"continuePendingWork,omitempty"` // Total number of persisted events in the session at the time of resume EventCount int64 `json:"eventCount"` // On-disk byte size of the session's persisted events.jsonl file at resume time; omitted when the file does not exist or cannot be stat'd EventsFileSizeBytes *int64 `json:"eventsFileSizeBytes,omitempty"` + // Provider of selectedModel at resume time, when explicitly selected. + ProviderID *string `json:"providerId,omitempty"` // Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") ReasoningEffort *string `json:"reasoningEffort,omitempty"` + // True when the reasoning effort is a managed-policy default bound to reasoningEffortModel. Omitted for agent-authored, user-authored, independent, and legacy effort. + ReasoningEffortManaged *bool `json:"reasoningEffortManaged,omitempty"` + // Model that owns effort embedded in an authored model selection. Omitted for independent reasoning-effort overrides and legacy events. + ReasoningEffortModel *string `json:"reasoningEffortModel,omitempty"` // Reasoning summary mode used for model calls, if applicable (e.g. "none", "concise", "detailed") ReasoningSummary *ReasoningSummary `json:"reasoningSummary,omitempty"` // Whether this session supports remote steering via GitHub @@ -2726,6 +2884,9 @@ func (*SessionSnapshotRewindData) Type() SessionEventType { // Session termination metrics including usage statistics, code changes, and shutdown reason type SessionShutdownData struct { + // Complete accounting state captured atomically with receipt watermarks. + // Internal: AccountingSnapshot is part of the SDK's internal API surface and is not intended for external use. + AccountingSnapshot *UsageGetMetricsResult `json:"accountingSnapshot,omitempty"` // Per-agent usage breakdown, keyed by agent instance identifier. The main conversation uses the stable key `main`. AgentMetrics map[string]ShutdownAgentMetric `json:"agentMetrics,omitzero"` // Aggregate code change metrics for the session @@ -2742,6 +2903,8 @@ type SessionShutdownData struct { EventsFileSizeBytes *int64 `json:"eventsFileSizeBytes,omitempty"` // Per-model usage breakdown, keyed by model identifier ModelMetrics map[string]ShutdownModelMetric `json:"modelMetrics"` + // Provider/model accounting preserved for replay without consulting current provider selection. + ProviderModelMetrics []UsageMetricsProviderModelMetric `json:"providerModelMetrics,omitzero"` // Unix timestamp (milliseconds) when the session started SessionStartTime int64 `json:"sessionStartTime"` // Whether the session ended normally ("routine") or due to a crash/fatal error ("error") @@ -2760,6 +2923,8 @@ type SessionShutdownData struct { // Total number of premium API requests used during the session // Internal: TotalPremiumRequests is part of the SDK's internal API surface and is not intended for external use. TotalPremiumRequests *float64 `json:"totalPremiumRequests,omitempty"` + // Per-source accounting sequence watermarks subsuming preceding usage receipts. + UsageAccountingWatermarks map[string]int64 `json:"usageAccountingWatermarks,omitzero"` } func (*SessionShutdownData) sessionEventData() {} @@ -2865,19 +3030,6 @@ func (*AssistantStreamingDeltaData) Type() SessionEventType { return SessionEventTypeAssistantStreamingDelta } -// Streaming tool execution output for incremental result display -type ToolExecutionPartialResultData struct { - // Incremental output chunk from the running tool - PartialOutput string `json:"partialOutput"` - // Tool call ID this partial result belongs to - ToolCallID string `json:"toolCallId"` -} - -func (*ToolExecutionPartialResultData) sessionEventData() {} -func (*ToolExecutionPartialResultData) Type() SessionEventType { - return SessionEventTypeToolExecutionPartialResult -} - // Streaming tool-call input delta for incremental tool-call updates type AssistantToolCallDeltaData struct { // Raw provider tool input fragment to append for this tool call. Function/tool-use providers stream serialized JSON argument text (so newlines inside JSON string values may appear as escaped `\n` until the accumulated JSON is parsed); custom tool calls stream raw custom input. @@ -3070,6 +3222,9 @@ func (*SessionModelDeselectedData) Type() SessionEventType { type ToolExecutionCompleteData struct { // Error details when the tool execution failed Error *ToolExecutionCompleteError `json:"error,omitempty"` + // Experimental. File mutations actually committed by a built-in file editing tool, in execution order. Present on successful edits and on partial failures when earlier mutations were committed. Paths are absolute in the session filesystem namespace. + // Experimental: FileEdits is part of an experimental API and may change or be removed. + FileEdits []ToolExecutionCompleteFileEdit `json:"fileEdits,omitzero"` // Experimental HydraFusion attribution for this tool completion. // Experimental: Fusion is part of an experimental API and may change or be removed. Fusion *FusionAttribution `json:"fusion,omitempty"` @@ -3115,6 +3270,9 @@ func (*ToolExecutionCompleteData) Type() SessionEventType { type ToolExecutionProgressData struct { // Human-readable progress status message (e.g., from an MCP server) ProgressMessage string `json:"progressMessage"` + // Client-only structured progress metadata. Not model-facing tool output. + // Experimental: StructuredContent is part of an experimental API and may change or be removed. + StructuredContent any `json:"structuredContent,omitempty"` // Tool call ID this progress notification belongs to ToolCallID string `json:"toolCallId"` } @@ -3332,6 +3490,8 @@ func (*SessionWorkspaceFileChangedData) Type() SessionEventType { type AssistantMessageReasoningBlocks struct { // Provider-native reasoning items or content blocks preserved verbatim, in order. A single response can carry several, and provider signatures or identifiers may depend on their exact content and ordering. Blocks []any `json:"blocks,omitzero"` + // Anthropic Messages assistant block ordering preserved when the legacy reasoning-only representation cannot reproduce it exactly. Thinking and text blocks remain verbatim; tool-use entries retain identity and a payload fingerprint when later signed reasoning depends on them, and are hydrated from the message's tool requests during replay. + OrderedBlocks []any `json:"orderedBlocks,omitzero"` // Model provider that produced these reasoning blocks. Provider string `json:"provider"` } @@ -3615,6 +3775,8 @@ type CommandsChangedCommand struct { // Token usage breakdown for the compaction LLM call (aligned with assistant.usage format) type CompactionCompleteCompactionTokensUsed struct { + // Whether the compaction's credit amount was reported for all, some, or none of its calls. + AiCreditsStatus *AiCreditsStatus `json:"aiCreditsStatus,omitempty"` // Cached input tokens reused in the compaction LLM call CacheReadTokens *int64 `json:"cacheReadTokens,omitempty"` // Tokens written to prompt cache in the compaction LLM call @@ -3628,8 +3790,12 @@ type CompactionCompleteCompactionTokensUsed struct { InputTokens *int64 `json:"inputTokens,omitempty"` // Model identifier used for the compaction LLM call Model *string `json:"model,omitempty"` + // Model display name captured for the compaction call. + ModelDisplayName *string `json:"modelDisplayName,omitempty"` // Output tokens produced by the compaction LLM call OutputTokens *int64 `json:"outputTokens,omitempty"` + // Provider captured for the compaction call; absent when attribution is unknown. + Provider *ModelProviderRef `json:"provider,omitempty"` } // Per-request cost and usage data from the CAPI copilot_usage response field @@ -3872,6 +4038,78 @@ type HookEndError struct { Stack *string `json:"stack,omitempty"` } +// Exact runtime-owned question or reviewed plan paired with the typed response that settled it. +type HumanResponseRecordedResponse interface { + humanResponseRecordedResponse() + ResponseKind() HumanResponseRecordedResponseResponseKind +} + +type RawHumanResponseRecordedResponse struct { + Discriminator HumanResponseRecordedResponseResponseKind + Raw json.RawMessage +} + +func (RawHumanResponseRecordedResponse) humanResponseRecordedResponse() {} +func (r RawHumanResponseRecordedResponse) ResponseKind() HumanResponseRecordedResponseResponseKind { + return r.Discriminator +} + +type HumanResponseRecordedResponseAskUser struct { + // Exact answer content accepted from the user. + Content map[string]any `json:"content"` + // Exact question displayed to the user. + Message string `json:"message"` + // Exact response schema displayed to the user. + RequestedSchema ElicitationRequestedSchema `json:"requestedSchema"` +} + +func (HumanResponseRecordedResponseAskUser) humanResponseRecordedResponse() {} +func (HumanResponseRecordedResponseAskUser) ResponseKind() HumanResponseRecordedResponseResponseKind { + return HumanResponseRecordedResponseResponseKindAskUser +} + +type HumanResponseRecordedResponseExitPlanMode struct { + // Actions offered by the plan review UI. + Actions []ExitPlanModeAction `json:"actions"` + // Whether the user approved the reviewed plan. + Approved bool `json:"approved"` + // Whether the selected response requested edit auto-approval. + AutoApproveEdits *bool `json:"autoApproveEdits,omitempty"` + // Exact feedback submitted with the plan decision, when present. + Feedback *string `json:"feedback,omitempty"` + // Exact full plan content available from the review UI. + PlanContent string `json:"planContent"` + // Action the plan review UI recommended. + RecommendedAction ExitPlanModeAction `json:"recommendedAction"` + // Action selected by the user, when applicable. + SelectedAction *ExitPlanModeAction `json:"selectedAction,omitempty"` + // Exact plan summary displayed to the user. + Summary string `json:"summary"` +} + +func (HumanResponseRecordedResponseExitPlanMode) humanResponseRecordedResponse() {} +func (HumanResponseRecordedResponseExitPlanMode) ResponseKind() HumanResponseRecordedResponseResponseKind { + return HumanResponseRecordedResponseResponseKindExitPlanMode +} + +type HumanResponseRecordedResponseUserInput struct { + // Whether the displayed request allowed a free-form answer. + AllowFreeform *bool `json:"allowFreeform,omitempty"` + // Exact selected or free-form answer submitted by the user. + Answer string `json:"answer"` + // Exact choices displayed to the user, when the request offered choices. + Choices []string `json:"choices,omitzero"` + // Exact question displayed to the user. + Question string `json:"question"` + // Whether the answer was typed as free-form text rather than selected from the displayed choices. + WasFreeform bool `json:"wasFreeform"` +} + +func (HumanResponseRecordedResponseUserInput) humanResponseRecordedResponse() {} +func (HumanResponseRecordedResponseUserInput) ResponseKind() HumanResponseRecordedResponseResponseKind { + return HumanResponseRecordedResponseResponseKindUserInput +} + // Set when the underlying tools/call threw an error before returning a CallToolResult type MCPAppToolCallCompleteError struct { // Human-readable error message @@ -3950,6 +4188,10 @@ type MCPServersLoadedServer struct { // Content-free structural summary of the failing request for diagnosing malformed 4xx calls type ModelCallFailureRequestFingerprint struct { + // Summed byte length of opaque or encrypted reasoning payloads + EncryptedContentBytes *int64 `json:"encryptedContentBytes,omitempty"` + // Summed byte length of inline image payloads (data URLs and base64 sources) + ImageBytes *int64 `json:"imageBytes,omitempty"` // Total number of image content parts ImagePartCount int64 `json:"imagePartCount"` // Image parts whose media type cannot be determined (rejected by strict providers) @@ -3960,6 +4202,8 @@ type ModelCallFailureRequestFingerprint struct { MessageCount int64 `json:"messageCount"` // Tool calls whose name is missing or empty (rejected by strict providers) NamelessToolCallCount int64 `json:"namelessToolCallCount"` + // Number of messages carrying opaque or encrypted reasoning + ReasoningItemCount *int64 `json:"reasoningItemCount,omitempty"` // Total number of tool calls across assistant messages ToolCallCount int64 `json:"toolCallCount"` // Number of "tool" result messages in the request @@ -4314,6 +4558,8 @@ type PermissionPromptRequestWrite struct { CanOfferSessionApproval bool `json:"canOfferSessionApproval"` // Unified diff showing the proposed changes Diff string `json:"diff"` + // Complete before/after text previews captured while planning this request. Omitted when a complete preview is unavailable, including pre-read sandbox requests and non-UTF-8 files. Missing sides mean creation or deletion, not unavailable content. + FileEdits []PermissionWriteFileEdit `json:"fileEdits,omitzero"` // Path of the file being written to FileName string `json:"fileName"` // Human-readable description of the intended file change @@ -4643,6 +4889,8 @@ type PermissionRequestWrite struct { CanOfferSessionApproval bool `json:"canOfferSessionApproval"` // Unified diff showing the proposed changes Diff string `json:"diff"` + // Complete before/after text previews captured while planning this request. Omitted when a complete preview is unavailable, including pre-read sandbox requests and non-UTF-8 files. Missing sides mean creation or deletion, not unavailable content. + FileEdits []PermissionWriteFileEdit `json:"fileEdits,omitzero"` // Path of the file being written to FileName string `json:"fileName"` // Human-readable description of the intended file change @@ -4841,6 +5089,22 @@ type PermissionSandboxPathGrant struct { RemovedReadonlyPaths []string `json:"removedReadonlyPaths,omitzero"` } +// A named, complete UTF-8 text snapshot for a pending file operation. +type PermissionWriteFileContent struct { + // Complete file contents, including an empty string for an empty file. + Content string `json:"content"` + // Absolute path in the session filesystem namespace. + Path string `json:"path"` +} + +// Complete text snapshots for one proposed file change. At least one side is present. Different before and after paths describe a move. +type PermissionWriteFileEdit struct { + // Complete file content the operation plans to write. Absent only when the file is being deleted. + After *PermissionWriteFileContent `json:"after,omitempty"` + // File content read while planning the operation. Absent only when the file is being created. + Before *PermissionWriteFileContent `json:"before,omitempty"` +} + // A model-facing binary result as persisted: full inline data, a size-omitted marker, or a deduplicated asset reference // Experimental: PersistedBinaryResult is part of an experimental API and may change or be removed. type PersistedBinaryResult interface { @@ -5399,6 +5663,15 @@ type ToolExecutionCompleteError struct { Remediation *RemediationAction `json:"remediation,omitempty"` } +// A file mutation that was actually committed by a built-in file editing tool. +// Experimental: ToolExecutionCompleteFileEdit is part of an experimental API and may change or be removed. +type ToolExecutionCompleteFileEdit struct { + // Kind of mutation committed at this path. + Kind ToolExecutionCompleteFileEditKind `json:"kind"` + // Absolute path in the session filesystem namespace. + Path string `json:"path"` +} + // Tool execution result on success type ToolExecutionCompleteResult struct { // Model-facing binary results (base64 inline or size-omitted markers) sent to the LLM for this tool call @@ -5560,6 +5833,16 @@ type ToolExecutionStartToolDescriptionMetaUI struct { Visibility []ToolExecutionStartToolDescriptionMetaUIVisibility `json:"visibility,omitzero"` } +// Ordered accounting identity assigned under the source session's emission lock. +type UsageAccountingIdentity struct { + // Monotonically increasing sequence within the source session. + Sequence int64 `json:"sequence"` + // Session that assigned this accounting sequence. + SourceSessionID string `json:"sourceSessionId"` + // Existing API call identifier, or a runtime-generated identity when none was supplied. + UsageID string `json:"usageId"` +} + // Internal prompt-cache expiration state for one model // Internal: UsageCheckpointModelCacheState is an internal SDK API and is not part of the public surface. type UsageCheckpointModelCacheState struct { @@ -6037,15 +6320,51 @@ const ( HandoffSourceTypeRemote HandoffSourceType = "remote" ) +// Controlled provenance for a typed runtime response. Only `human_response`, minted by a trusted direct-interaction ingress, is human authorization evidence. +type HumanResponseActor string + +const ( + // A host or SDK automation submitted the response without direct human interaction. + HumanResponseActorHostAutomation HumanResponseActor = "host_automation" + // A built-in trusted client submitted the response after direct human interaction. + HumanResponseActorHumanResponse HumanResponseActor = "human_response" + // The response came through a legacy or otherwise unattributed ingress. + HumanResponseActorUnknown HumanResponseActor = "unknown" +) + +// ResponseKind discriminator for HumanResponseRecordedResponse. +type HumanResponseRecordedResponseResponseKind string + +const ( + HumanResponseRecordedResponseResponseKindAskUser HumanResponseRecordedResponseResponseKind = "ask_user" + HumanResponseRecordedResponseResponseKindExitPlanMode HumanResponseRecordedResponseResponseKind = "exit_plan_mode" + HumanResponseRecordedResponseResponseKindUserInput HumanResponseRecordedResponseResponseKind = "user_input" +) + +// Current phase of organization-required plugin preparation. +// Experimental: ManagedPluginProgressPhase is part of an experimental API and may change or be removed. +type ManagedPluginProgressPhase string + +const ( + // Managed plugin preparation has completed. + ManagedPluginProgressPhaseComplete ManagedPluginProgressPhase = "complete" + // Managed settings are still resolving before plugin preparation can be planned. + ManagedPluginProgressPhaseInitializing ManagedPluginProgressPhase = "initializing" + // Required plugin content is being installed. + ManagedPluginProgressPhaseInstalling ManagedPluginProgressPhase = "installing" + // Installed required plugin content is being updated. + ManagedPluginProgressPhaseUpdating ManagedPluginProgressPhase = "updating" +) + // The category of runtime action that enterprise managed settings governed (blocked or capped) type ManagedSettingsEnforcedAction string const ( - // An attempt to turn on a bypass-permissions ("yolo") escalation was refused or capped because policy disables bypass-permissions mode. + // An attempt to enter a permission mode governed by managed policy was refused or capped. The `setting` and `escalation` fields identify whether this was bypass permissions or Assisted Permissions. ManagedSettingsEnforcedActionBypassPermissionsBlocked ManagedSettingsEnforcedAction = "bypass_permissions_blocked" ) -// For a `bypass_permissions_blocked` action, which permission-escalation primitive was refused +// For a `bypass_permissions_blocked` action, which permission-mode or escalation primitive was refused type ManagedSettingsEnforcedEscalation string const ( @@ -6053,7 +6372,7 @@ const ( ManagedSettingsEnforcedEscalationAllowAll ManagedSettingsEnforcedEscalation = "allow_all" // Automatic approval of all tool permission requests. ManagedSettingsEnforcedEscalationApproveAll ManagedSettingsEnforcedEscalation = "approve_all" - // Assisted mode — keeps normal prompt paths and adds an LLM recommendation, distinct from allow-all. + // Assisted Permissions — uses an LLM review to reduce prompts, distinct from allow-all and not a hard security boundary. ManagedSettingsEnforcedEscalationAssistedApproval ManagedSettingsEnforcedEscalation = "assisted_approval" // A server-wide MCP "Always Allow" (or `--allow-tool `) blanket that would auto-approve every tool from an MCP server. Capped to per-tool approval; each tool still prompts. ManagedSettingsEnforcedEscalationServerWideMCPApproval ManagedSettingsEnforcedEscalation = "server_wide_mcp_approval" @@ -6212,6 +6531,22 @@ const ( ModelCallFinishedOutcomeSuccess ModelCallFinishedOutcome = "success" ) +// Why a WebSocket-capable model call was carried by the HTTP fallback +type ModelCallWebSocketFallbackReason string + +const ( + // The WebSocket returned a retryable API error. + ModelCallWebSocketFallbackReasonAPIError ModelCallWebSocketFallbackReason = "api_error" + // The WebSocket connection could not be established. + ModelCallWebSocketFallbackReasonConnectFailed ModelCallWebSocketFallbackReason = "connect_failed" + // No usable WebSocket connection was available for the request. + ModelCallWebSocketFallbackReasonConnectionUnavailable ModelCallWebSocketFallbackReason = "connection_unavailable" + // Sending the request over the WebSocket failed. + ModelCallWebSocketFallbackReasonSendFailed ModelCallWebSocketFallbackReason = "send_failed" + // The WebSocket transport failed before any output reached the consumer. + ModelCallWebSocketFallbackReasonTransportFailed ModelCallWebSocketFallbackReason = "transport_failed" +) + // Why the session no longer has an explicitly selected model. type ModelDeselectedReason string @@ -6274,6 +6609,14 @@ const ( PermissionApprovalEvaluationReasonCodeArgumentBindingUnreviewable PermissionApprovalEvaluationReasonCode = "argument-binding-unreviewable" // The judge was skipped because authorization extraction could not safely establish a complete recent history. PermissionApprovalEvaluationReasonCodeAuthorizationHistoryIncomplete PermissionApprovalEvaluationReasonCode = "authorization-history-incomplete" + // A code source was excluded from review by content exclusion policy. + PermissionApprovalEvaluationReasonCodeContentExcluded PermissionApprovalEvaluationReasonCode = "content-excluded" + // The shell command used a code source computed at run time. + PermissionApprovalEvaluationReasonCodeDynamicSource PermissionApprovalEvaluationReasonCode = "dynamic-source" + // A code-bearing executable exceeded the binding size limit. + PermissionApprovalEvaluationReasonCodeExecutableTooLarge PermissionApprovalEvaluationReasonCode = "executable-too-large" + // A code-bearing executable could not be inspected. + PermissionApprovalEvaluationReasonCodeExecutableUnavailable PermissionApprovalEvaluationReasonCode = "executable-unavailable" // Assisted approval was inactive for this request. PermissionApprovalEvaluationReasonCodeInactive PermissionApprovalEvaluationReasonCode = "inactive" // The request inherited an outcome from another decision. @@ -6310,6 +6653,8 @@ const ( PermissionApprovalEvaluationReasonCodeShellEnvironmentUnreviewable PermissionApprovalEvaluationReasonCode = "shell-environment-unreviewable" // The script snapshot exceeded the size limit. PermissionApprovalEvaluationReasonCodeTooLarge PermissionApprovalEvaluationReasonCode = "too-large" + // The shell command referenced more code sources than can be reviewed. + PermissionApprovalEvaluationReasonCodeTooManySources PermissionApprovalEvaluationReasonCode = "too-many-sources" // Script review was unavailable. PermissionApprovalEvaluationReasonCodeUnavailable PermissionApprovalEvaluationReasonCode = "unavailable" // Attribution is missing or outside the supported vocabulary. @@ -6320,6 +6665,10 @@ const ( PermissionApprovalEvaluationReasonCodeUnrepresentablePath PermissionApprovalEvaluationReasonCode = "unrepresentable-path" // The script invocation could not be reviewed. PermissionApprovalEvaluationReasonCodeUnreviewableScriptInvocation PermissionApprovalEvaluationReasonCode = "unreviewable-script-invocation" + // The shell command could not be analyzed for execution evidence. + PermissionApprovalEvaluationReasonCodeUnsupportedCommandShape PermissionApprovalEvaluationReasonCode = "unsupported-command-shape" + // The shell command used a code source that cannot be bound for review. + PermissionApprovalEvaluationReasonCodeUnsupportedSource PermissionApprovalEvaluationReasonCode = "unsupported-source" ) // Direction stored in a historical extractor claim. Current runtimes do not apply it. @@ -6478,7 +6827,7 @@ const ( PlanChangedOperationUpdate PlanChangedOperation = "update" ) -// Auto preferences that Copilot API can recommend. +// Enabled Auto preferences that Copilot API can recommend. type RecommendedAutoTier string const ( @@ -6641,6 +6990,19 @@ const ( ToolExecutionCompleteContentTypeText ToolExecutionCompleteContentType = "text" ) +// Kind of file mutation committed by a built-in editing tool. +// Experimental: ToolExecutionCompleteFileEditKind is part of an experimental API and may change or be removed. +type ToolExecutionCompleteFileEditKind string + +const ( + // A file was created. + ToolExecutionCompleteFileEditKindCreate ToolExecutionCompleteFileEditKind = "create" + // A file was deleted. + ToolExecutionCompleteFileEditKindDelete ToolExecutionCompleteFileEditKind = "delete" + // A file was written by an edit operation. + ToolExecutionCompleteFileEditKindEdit ToolExecutionCompleteFileEditKind = "edit" +) + // Allowed values for the `ToolExecutionCompleteToolDescriptionMetaUIVisibility` enumeration. type ToolExecutionCompleteToolDescriptionMetaUIVisibility string @@ -6661,6 +7023,18 @@ const ( ToolExecutionStartToolDescriptionMetaUIVisibilityModel ToolExecutionStartToolDescriptionMetaUIVisibility = "model" ) +// Shell output source. Terminal output has no separate stdout/stderr attribution. +type ToolShellOutputStream string + +const ( + // Output from the shell command's standard error stream. + ToolShellOutputStreamStderr ToolShellOutputStream = "stderr" + // Output from the shell command's standard output stream. This is the default when stream is omitted. + ToolShellOutputStreamStdout ToolShellOutputStream = "stdout" + // Inherently merged output that cannot be attributed separately to stdout or stderr. + ToolShellOutputStreamTerminal ToolShellOutputStream = "terminal" +) + // Lifecycle phase for a Rust-owned ephemeral query stream. // Experimental: UIEphemeralQueryPhase is part of an experimental API and may change or be removed. type UIEphemeralQueryPhase string diff --git a/go/session.go b/go/session.go index 1c7e21c068..870b3f836f 100644 --- a/go/session.go +++ b/go/session.go @@ -62,11 +62,14 @@ type Session struct { transcriptRecovery *TranscriptRecoveryReport client *jsonrpc2.Client clientSessionAPIs *rpc.ClientSessionAPIHandlers - handlers []sessionHandler + handlers []*sessionHandler nextHandlerID uint64 handlerMutex sync.RWMutex + gracefulRetirement bool toolHandlers map[string]ToolHandler toolHandlersM sync.RWMutex + setToolsOnce sync.Once + setToolsGate chan struct{} pendingExternalTools map[string]*pendingExternalTool pendingExternalToolsM sync.Mutex externalToolsClosed bool @@ -91,6 +94,8 @@ type Session struct { elicitationMu sync.RWMutex canvasHandler CanvasHandler canvasMu sync.RWMutex + skillProvider SkillProvider + skillProviderMu sync.RWMutex bearerTokenProviders map[string]BearerTokenProvider bearerTokenMu sync.RWMutex releaseGitHubTokenProvider func() @@ -219,6 +224,24 @@ func (s *Session) getCanvasHandler() CanvasHandler { return s.canvasHandler } +func (s *Session) registerSkillProvider(provider SkillProvider) { + s.skillProviderMu.Lock() + defer s.skillProviderMu.Unlock() + s.skillProvider = provider +} + +func (s *Session) getSkillProvider() SkillProvider { + s.skillProviderMu.RLock() + defer s.skillProviderMu.RUnlock() + return s.skillProvider +} + +func (s *Session) clearSkillProvider() { + s.skillProviderMu.Lock() + s.skillProvider = nil + s.skillProviderMu.Unlock() +} + // registerBearerTokenProviders installs per-provider [BearerTokenProvider] callbacks // for BYOK providers configured with managed-identity / on-demand bearer-token // auth, keyed by provider name. @@ -404,7 +427,7 @@ func newSession( managedSettings: managedSettings, client: client, clientSessionAPIs: &rpc.ClientSessionAPIHandlers{}, - handlers: make([]sessionHandler, 0), + handlers: make([]*sessionHandler, 0), toolHandlers: make(map[string]ToolHandler), commandHandlers: make(map[string]CommandHandler), eventCh: make(chan SessionEvent, 128), @@ -491,7 +514,7 @@ func (s *Session) SendPrompt(ctx context.Context, prompt string) (string, error) // // Events are still delivered to handlers registered via [Session.On] while waiting. // Synchronous handlers registered before this call finish processing the completing -// root session.idle event before it returns successfully. +// root session.idle event before it returns successfully, unless the session retires. // This does not wait for asynchronous work started by a handler. // Sub-agent events with a non-empty AgentID do not complete the wait or supply its reply. // @@ -501,7 +524,7 @@ func (s *Session) SendPrompt(ctx context.Context, prompt string) (string, error) // Controls how long to wait; does not abort in-flight agent work. // // Returns the final assistant message event, or nil if none was received. -// Returns an error if the timeout is reached or the connection fails. +// Returns an error if the timeout is reached, the session is closed, or the connection fails. // // Example: // @@ -526,34 +549,56 @@ func (s *Session) SendAndWait(ctx context.Context, options MessageOptions) (*Ses defer cancel() } - idleCh := make(chan struct{}, 1) - errCh := make(chan error, 1) + type outcome struct { + event *SessionEvent + err error + } + terminalCh := make(chan outcome, 1) + dispatchedCh := make(chan struct{}, 1) var lastAssistantMessage *SessionEvent - var mu sync.Mutex + completed := false + finish := func(result outcome) { + if !completed { + completed = true + terminalCh <- result + } + } - unsubscribe := s.On(func(event SessionEvent) { - if event.AgentID != nil && *event.AgentID != "" { + // Observe the result before user callbacks can retire the current dispatch. + unobserve := s.on(func(event SessionEvent) { + if completed || (event.AgentID != nil && *event.AgentID != "") { return } switch d := event.Data.(type) { case *AssistantMessageData: - mu.Lock() eventCopy := event lastAssistantMessage = &eventCopy - mu.Unlock() case *SessionIdleData: if d.Mode != nil && *d.Mode == SessionModeAutopilot { break } - select { - case idleCh <- struct{}{}: - default: - } + finish(outcome{event: lastAssistantMessage}) case *SessionErrorData: - select { - case errCh <- fmt.Errorf("session error: %s", d.Message): - default: + finish(outcome{err: fmt.Errorf("session error: %s", d.Message)}) + } + }, true) + defer unobserve() + unsubscribe := s.On(func(event SessionEvent) { + if event.AgentID != nil && *event.AgentID != "" { + return + } + switch data := event.Data.(type) { + case *SessionIdleData: + if data.Mode != nil && *data.Mode == SessionModeAutopilot { + return } + case *SessionErrorData: + default: + return + } + select { + case dispatchedCh <- struct{}{}: + default: } }) defer unsubscribe() @@ -564,16 +609,27 @@ func (s *Session) SendAndWait(ctx context.Context, options MessageOptions) (*Ses } select { - case <-idleCh: - mu.Lock() - result := lastAssistantMessage - mu.Unlock() - return result, nil - case err := <-errCh: - return nil, err + case <-dispatchedCh: + case <-s.eventDone: + s.handlerMutex.RLock() + graceful := s.gracefulRetirement + s.handlerMutex.RUnlock() + if !graceful { + select { + case <-dispatchedCh: + default: + return nil, errors.New("session closed before response completed") + } + } case <-ctx.Done(): return nil, fmt.Errorf("waiting for session.idle: %w", ctx.Err()) } + select { + case result := <-terminalCh: + return result.event, result.err + default: + return nil, errors.New("session closed before response completed") + } } // SendPromptAndWait is a convenience wrapper for [Session.SendAndWait] that @@ -593,6 +649,8 @@ func (s *Session) SendPromptAndWait(ctx context.Context, prompt string) (*Sessio // // The returned function can be called to unsubscribe the handler. It is safe // to call the unsubscribe function multiple times. +// Unsubscribing also removes an entry from dispatch snapshots if it has not started. +// Registration after the session closes is logged and returns a no-op unsubscribe. // // Example: // @@ -608,12 +666,28 @@ func (s *Session) SendPromptAndWait(ctx context.Context, prompt string) (*Sessio // // Later, to stop receiving events: // unsubscribe() func (s *Session) On(handler SessionEventHandler) func() { + return s.on(handler, false) +} + +func (s *Session) on(handler SessionEventHandler, prepend bool) func() { s.handlerMutex.Lock() defer s.handlerMutex.Unlock() + select { + case <-s.eventDone: + log.Printf("Cannot register an event handler on closed session %s", s.SessionID) + return func() {} + default: + } + id := s.nextHandlerID s.nextHandlerID++ - s.handlers = append(s.handlers, sessionHandler{id: id, fn: handler}) + entry := &sessionHandler{id: id, fn: handler} + if prepend { + s.handlers = append([]*sessionHandler{entry}, s.handlers...) + } else { + s.handlers = append(s.handlers, entry) + } // Return unsubscribe function return func() { @@ -622,6 +696,7 @@ func (s *Session) On(handler SessionEventHandler) func() { for i, h := range s.handlers { if h.id == id { + h.fn = nil s.handlers = append(s.handlers[:i], s.handlers[i+1:]...) break } @@ -636,16 +711,32 @@ func (s *Session) On(handler SessionEventHandler) func() { // // This method is internal and typically called when creating a session with tools. func (s *Session) registerTools(tools []Tool) { - s.toolHandlersM.Lock() - defer s.toolHandlersM.Unlock() + if err := s.replaceToolHandlers(buildToolHandlerMap(tools)); err != nil { + log.Printf("Cannot register tools on session %s: %v", s.SessionID, err) + } +} - s.toolHandlers = make(map[string]ToolHandler) +func buildToolHandlerMap(tools []Tool) map[string]ToolHandler { + handlers := make(map[string]ToolHandler) for _, tool := range tools { if tool.Name == "" || tool.Handler == nil { continue } - s.toolHandlers[tool.Name] = tool.Handler + handlers[tool.Name] = tool.Handler } + return handlers +} + +func (s *Session) replaceToolHandlers(handlers map[string]ToolHandler) error { + s.toolHandlersM.Lock() + defer s.toolHandlersM.Unlock() + select { + case <-s.eventDone: + return errors.New("session closed before tool handlers could be installed") + default: + } + s.toolHandlers = handlers + return nil } // getToolHandler retrieves a registered tool handler by name. @@ -717,14 +808,16 @@ func (s *Session) registerExitPlanModeHandler(handler ExitPlanModeRequestHandler s.exitPlanModeHandler = handler } -func (s *Session) getExitPlanModeHandler() ExitPlanModeRequestHandler { - s.exitPlanModeMu.RLock() - defer s.exitPlanModeMu.RUnlock() - return s.exitPlanModeHandler -} - func (s *Session) handleExitPlanModeRequest(request ExitPlanModeRequest) (ExitPlanModeResult, error) { - handler := s.getExitPlanModeHandler() + s.exitPlanModeMu.RLock() + select { + case <-s.eventDone: + s.exitPlanModeMu.RUnlock() + return ExitPlanModeResult{}, errors.New("session closed before exit-plan-mode request") + default: + } + handler := s.exitPlanModeHandler + s.exitPlanModeMu.RUnlock() if handler == nil { return ExitPlanModeResult{Approved: true}, nil } @@ -738,14 +831,16 @@ func (s *Session) registerAutoModeSwitchHandler(handler AutoModeSwitchRequestHan s.autoModeSwitchHandler = handler } -func (s *Session) getAutoModeSwitchHandler() AutoModeSwitchRequestHandler { - s.autoModeSwitchMu.RLock() - defer s.autoModeSwitchMu.RUnlock() - return s.autoModeSwitchHandler -} - func (s *Session) handleAutoModeSwitchRequest(request AutoModeSwitchRequest) (AutoModeSwitchResponse, error) { - handler := s.getAutoModeSwitchHandler() + s.autoModeSwitchMu.RLock() + select { + case <-s.eventDone: + s.autoModeSwitchMu.RUnlock() + return "", errors.New("session closed before auto-mode-switch request") + default: + } + handler := s.autoModeSwitchHandler + s.autoModeSwitchMu.RUnlock() if handler == nil { return AutoModeSwitchResponseNo, nil } @@ -765,17 +860,18 @@ func (s *Session) registerHooks(hooks *SessionHooks) { s.hooks = hooks } -// getHooks returns the currently registered hooks, or nil. -func (s *Session) getHooks() *SessionHooks { - s.hooksMux.RLock() - defer s.hooksMux.RUnlock() - return s.hooks -} - // handleHooksInvoke handles a hook invocation from the Copilot CLI. // This is an internal method called by the SDK when the CLI invokes a hook. func (s *Session) handleHooksInvoke(hookType string, rawInput json.RawMessage) (any, error) { - hooks := s.getHooks() + s.hooksMux.RLock() + select { + case <-s.eventDone: + s.hooksMux.RUnlock() + return nil, errors.New("session closed before hook invocation") + default: + } + hooks := s.hooks + s.hooksMux.RUnlock() if hooks == nil { return nil, nil @@ -886,6 +982,26 @@ func (s *Session) handleHooksInvoke(hookType string, rawInput json.RawMessage) ( } return hooks.OnAgentStop(input, invocation) + case "subagentStart": + if hooks.OnSubagentStart == nil { + return nil, nil + } + var input SubagentStartHookInput + if err := json.Unmarshal(rawInput, &input); err != nil { + return nil, fmt.Errorf("invalid hook input: %w", err) + } + return hooks.OnSubagentStart(input, invocation) + + case "subagentStop": + if hooks.OnSubagentStop == nil { + return nil, nil + } + var input SubagentStopHookInput + if err := json.Unmarshal(rawInput, &input); err != nil { + return nil, fmt.Errorf("invalid hook input: %w", err) + } + return hooks.OnSubagentStop(input, invocation) + default: return nil, nil } @@ -918,6 +1034,12 @@ type systemMessageTransformResponse struct { // This is an internal method called by the SDK when the CLI requests section transforms. func (s *Session) handleSystemMessageTransform(sections map[string]systemMessageTransformSection) (systemMessageTransformResponse, error) { s.transformMu.Lock() + select { + case <-s.eventDone: + s.transformMu.Unlock() + return systemMessageTransformResponse{}, errors.New("session closed before system message transform") + default: + } callbacks := s.transformCallbacks s.transformMu.Unlock() @@ -1437,7 +1559,11 @@ func (s *Session) dispatchEvent(event SessionEvent) { broadcastHandled := false switch data := event.Data.(type) { case *ExternalToolRequestedData: - s.startExternalTool(data) + var agentID string + if event.AgentID != nil { + agentID = *event.AgentID + } + s.startExternalTool(data, agentID) broadcastHandled = true case *ExternalToolCompletedData: s.cancelExternalTool(data.RequestID) @@ -1459,6 +1585,12 @@ func (s *Session) dispatchEvent(event SessionEvent) { // others from receiving the event. func (s *Session) processEvents() { for { + select { + case <-s.eventDone: + return + default: + } + var event SessionEvent select { case event = <-s.eventCh: @@ -1467,13 +1599,23 @@ func (s *Session) processEvents() { } s.handlerMutex.RLock() - handlers := make([]SessionEventHandler, 0, len(s.handlers)) - for _, h := range s.handlers { - handlers = append(handlers, h.fn) - } + handlers := append([]*sessionHandler(nil), s.handlers...) s.handlerMutex.RUnlock() - for _, handler := range handlers { + for _, entry := range handlers { + s.handlerMutex.RLock() + select { + case <-s.eventDone: + s.handlerMutex.RUnlock() + return + default: + } + handler := entry.fn + s.handlerMutex.RUnlock() + if handler == nil { + continue + } + func() { defer func() { if r := recover(); r != nil { @@ -1486,11 +1628,77 @@ func (s *Session) processEvents() { } } -// stopEventProcessing stops the session event consumer without making an RPC. +// stopEventProcessing signals the consumer to stop without making an RPC or +// waiting for a user handler that may itself be stopping the client. +// It releases registered callbacks, including unadmitted event snapshot entries. // CreateSession/ResumeSession use this when a locally registered session fails // before it can be returned to the caller. func (s *Session) stopEventProcessing() { - s.closeOnce.Do(func() { close(s.eventDone) }) + s.retireEventProcessing(false) +} + +func (s *Session) retireEventProcessing(graceful bool) { + s.closeOnce.Do(func() { + s.handlerMutex.Lock() + s.gracefulRetirement = graceful + close(s.eventDone) + for _, handler := range s.handlers { + handler.fn = nil + } + s.handlers = nil + s.handlerMutex.Unlock() + + s.clearSkillProvider() + s.releaseGitHubTokenProviderRegistration() + + s.toolHandlersM.Lock() + s.toolHandlers = nil + s.toolHandlersM.Unlock() + + s.permissionMux.Lock() + s.permissionHandler = nil + s.permissionMux.Unlock() + + s.mcpAuthMu.Lock() + s.mcpAuthHandler = nil + s.mcpAuthMu.Unlock() + + s.userInputMux.Lock() + s.userInputHandler = nil + s.userInputMux.Unlock() + + s.exitPlanModeMu.Lock() + s.exitPlanModeHandler = nil + s.exitPlanModeMu.Unlock() + + s.autoModeSwitchMu.Lock() + s.autoModeSwitchHandler = nil + s.autoModeSwitchMu.Unlock() + + s.hooksMux.Lock() + s.hooks = nil + s.hooksMux.Unlock() + + s.transformMu.Lock() + s.transformCallbacks = nil + s.transformMu.Unlock() + + s.commandHandlersMu.Lock() + s.commandHandlers = nil + s.commandHandlersMu.Unlock() + + s.elicitationMu.Lock() + s.elicitationHandler = nil + s.elicitationMu.Unlock() + + s.canvasMu.Lock() + s.canvasHandler = nil + s.canvasMu.Unlock() + + s.bearerTokenMu.Lock() + s.bearerTokenProviders = nil + s.bearerTokenMu.Unlock() + }) } // handleBroadcastEvent handles broadcast request events by executing local handlers @@ -1585,7 +1793,7 @@ func (s *Session) handleBroadcastEvent(event SessionEvent) { } } -func (s *Session) startExternalTool(data *ExternalToolRequestedData) { +func (s *Session) startExternalTool(data *ExternalToolRequestedData, agentID string) { handler, ok := s.getToolHandler(data.ToolName) if !ok { return @@ -1619,7 +1827,7 @@ func (s *Session) startExternalTool(data *ExternalToolRequestedData) { s.pendingExternalTools[data.RequestID] = pending s.pendingExternalToolsM.Unlock() - go s.executeToolAndRespond(data.RequestID, data.ToolName, data.ToolCallID, data.Arguments, handler, pending) + go s.executeToolAndRespond(data.RequestID, data.ToolName, data.ToolCallID, agentID, data.Arguments, handler, pending) } func (s *Session) cancelExternalTool(requestID string) { @@ -1654,7 +1862,7 @@ func (s *Session) claimExternalTool(requestID string, pending *pendingExternalTo } // executeToolAndRespond executes a tool handler and sends the result back via RPC. -func (s *Session) executeToolAndRespond(requestID, toolName, toolCallID string, arguments any, handler ToolHandler, pending *pendingExternalTool) { +func (s *Session) executeToolAndRespond(requestID, toolName, toolCallID, agentID string, arguments any, handler ToolHandler, pending *pendingExternalTool) { ctx := pending.ctx defer func() { s.pendingExternalToolsM.Lock() @@ -1683,6 +1891,8 @@ func (s *Session) executeToolAndRespond(requestID, toolName, toolCallID string, ToolName: toolName, Arguments: arguments, TraceContext: ctx, + AgentID: agentID, + RequestID: requestID, } // The built-in tool-search tool receives a snapshot of the session's @@ -1865,6 +2075,7 @@ func (s *Session) GetEvents(ctx context.Context) ([]SessionEvent, error) { // log.Printf("Failed to disconnect session: %v", err) // } func (s *Session) Disconnect() error { + s.clearSkillProvider() s.cancelPendingExternalTools() result, err := s.client.Request(context.Background(), "session.detach", sessionDetachRequest{SessionID: s.SessionID}) if err == nil { @@ -1882,29 +2093,7 @@ func (s *Session) Disconnect() error { // Local cleanup always runs, even if the detach RPC failed, so callers // don't leak in-memory resources (event goroutines, registered // providers/handlers) just because the runtime couldn't be reached. - s.stopEventProcessing() - s.releaseGitHubTokenProviderRegistration() - - // Clear handlers - s.handlerMutex.Lock() - s.handlers = nil - s.handlerMutex.Unlock() - - s.toolHandlersM.Lock() - s.toolHandlers = nil - s.toolHandlersM.Unlock() - - s.permissionMux.Lock() - s.permissionHandler = nil - s.permissionMux.Unlock() - - s.commandHandlersMu.Lock() - s.commandHandlers = nil - s.commandHandlersMu.Unlock() - - s.elicitationMu.Lock() - s.elicitationHandler = nil - s.elicitationMu.Unlock() + s.retireEventProcessing(true) if err != nil { return fmt.Errorf("failed to disconnect session: %w", err) @@ -2041,6 +2230,107 @@ func (s *Session) SetModel(ctx context.Context, model string, opts *SetModelOpti return nil } +func (s *Session) setToolsSemaphore() chan struct{} { + s.setToolsOnce.Do(func() { + s.setToolsGate = make(chan struct{}, 1) + s.setToolsGate <- struct{}{} + }) + return s.setToolsGate +} + +func toolDefinitionsForSetTools(tools []Tool) []rpc.ProtocolExternalToolDefinition { + definitions := make([]rpc.ProtocolExternalToolDefinition, 0, len(tools)) + for _, tool := range tools { + definition := rpc.ProtocolExternalToolDefinition{ + Name: tool.Name, + Description: tool.Description, + Parameters: tool.Parameters, + Metadata: tool.Metadata, + } + if tool.OverridesBuiltInTool { + definition.OverridesBuiltInTool = &tool.OverridesBuiltInTool + } + if tool.SkipPermission { + definition.SkipPermission = &tool.SkipPermission + } + if tool.IsTerminal { + definition.IsTerminal = &tool.IsTerminal + } + if tool.Defer != "" { + deferPolicy := rpc.ProtocolExternalToolDefer(tool.Defer) + definition.Defer = &deferPolicy + } + definitions = append(definitions, definition) + } + return definitions +} + +// SetTools atomically replaces the externally implemented tools supplied by +// this client connection for the live session. Built-in tools, MCP/plugin tools, +// extension-discovered tools, subagent tools, and tools supplied by other +// connections are unchanged. +// +// The tools use the same [Tool] definitions and handlers as +// [Client.CreateSession] and [Client.ResumeSession]. The slice is a complete +// replacement for this connection: pass an empty slice to remove every tool this +// connection previously supplied. Tools with nil handlers are declaration-only. +// +// Handlers switch after the runtime accepts the replacement. Calls already +// running finish on the handlers that started them. If the runtime rejects the +// replacement, the previous handlers remain installed. Concurrent SetTools calls +// on the same session are sent and applied in call order; an earlier failure does +// not block later calls. If ctx is done before the request is sent, for example +// while an earlier SetTools call is still in flight, nothing is sent. Once the +// request is sent, ctx only bounds the wait: an accepted replacement still +// installs its handlers unless the session has since closed. +// +// The agent sees the new tools from its next model request, which can fall +// within a turn in progress. A model request already in flight was made with the +// previous tools, so the agent can still call a tool you removed. This session +// doesn't answer that call, and it can stay pending until the turn is aborted. If +// a running turn might still call a tool you remove, replace tools while the +// session is idle. +// +// Experimental: SetTools wraps the experimental session.tools.set RPC and may +// change or be removed. +func (s *Session) SetTools(ctx context.Context, tools []Tool) error { + definitions := toolDefinitionsForSetTools(tools) + handlers := buildToolHandlerMap(tools) + + gate := s.setToolsSemaphore() + select { + case <-gate: + case <-ctx.Done(): + return ctx.Err() + } + + if err := ctx.Err(); err != nil { + gate <- struct{}{} + return err + } + + done := make(chan error, 1) + go func() { + defer func() { gate <- struct{}{} }() + + _, err := s.RPC.Tools.Set(context.WithoutCancel(ctx), &rpc.ToolsSetRequest{ + Tools: definitions, + }) + if err != nil { + done <- fmt.Errorf("failed to set tools: %w", err) + return + } + done <- s.replaceToolHandlers(handlers) + }() + + select { + case err := <-done: + return err + case <-ctx.Done(): + return ctx.Err() + } +} + // SetAutoTier changes the Auto routing preference without changing the selected model. // // The runtime does not apply the preference immediately. It records the request and diff --git a/go/session_fs_provider.go b/go/session_fs_provider.go index 0f653f1a0f..1f00ecc83f 100644 --- a/go/session_fs_provider.go +++ b/go/session_fs_provider.go @@ -5,6 +5,7 @@ package copilot import ( + "encoding/base64" "errors" "os" "time" @@ -12,6 +13,9 @@ import ( "github.com/github/copilot-sdk/go/rpc" ) +const maxSessionFSBinaryBytes = (64*1024*1024 - 1024) / 4 * 3 +const maxSessionFSBinaryContentLength = (maxSessionFSBinaryBytes + 2) / 3 * 4 + // SessionFSProvider is the interface that SDK users implement to provide // a session filesystem. Methods use idiomatic Go error handling: return an // error for failures (the adapter maps os.ErrNotExist → ENOENT automatically). @@ -23,6 +27,7 @@ type SessionFSProvider interface { ReadFile(path string) (string, error) // WriteFile writes content to a file, creating it and parent directories if needed. // mode is an optional POSIX-style permission mode. Pass nil to use the OS default. + // Return a *SessionFSWriteFailure if the failed write changed the target. WriteFile(path string, content string, mode *int) error // AppendFile appends content to a file, creating it and parent directories if needed. // mode is an optional POSIX-style permission mode. Pass nil to use the OS default. @@ -48,6 +53,32 @@ type SessionFSProvider interface { Rename(src string, dest string) error } +// SessionFSWriteFailure reports that a failed WriteFile changed its target. +// Wrap the original error so its cause remains available to errors.Is and errors.As. +type SessionFSWriteFailure struct { + Err error +} + +func (e *SessionFSWriteFailure) Error() string { + if e == nil || e.Err == nil { + return "session filesystem write failed after changing the target" + } + return e.Err.Error() +} +func (e *SessionFSWriteFailure) Unwrap() error { + if e == nil { + return nil + } + return e.Err +} + +// SessionFSBinaryProvider is an optional interface for exact file bytes. +// Declare capabilities.binary only when the provider implements both methods. +type SessionFSBinaryProvider interface { + ReadFileBytes(path string) ([]byte, error) + WriteFileBytes(path string, content []byte, mode *int) error +} + // SessionFSSqliteProvider is an optional interface that a [SessionFSProvider] // may also implement to support per-session SQLite databases. The adapter // checks for this interface at runtime using a type assertion. If the @@ -129,6 +160,23 @@ func (a *sessionFSAdapter) ReadFile(request *rpc.SessionFSReadFileRequest) (*rpc return &rpc.SessionFSReadFileResult{Content: content}, nil } +func (a *sessionFSAdapter) ReadFileBytes(request *rpc.SessionFSReadFileBytesRequest) (*rpc.SessionFSReadFileBytesResult, error) { + provider, ok := a.provider.(SessionFSBinaryProvider) + if !ok { + return &rpc.SessionFSReadFileBytesResult{Error: toSessionFSError(errors.New("binary reads are not supported"))}, nil + } + bytes, err := provider.ReadFileBytes(request.Path) + if err != nil { + return &rpc.SessionFSReadFileBytesResult{Error: toSessionFSError(err)}, nil + } + if len(bytes) > maxSessionFSBinaryBytes { + return &rpc.SessionFSReadFileBytesResult{ + Error: toSessionFSError(errors.New("sessionFs.readFileBytes content exceeds the binary read limit")), + }, nil + } + return &rpc.SessionFSReadFileBytesResult{Content: base64.StdEncoding.EncodeToString(bytes)}, nil +} + func (a *sessionFSAdapter) WriteFile(request *rpc.SessionFSWriteFileRequest) (*rpc.SessionFSError, error) { var mode *int if request.Mode != nil { @@ -136,6 +184,41 @@ func (a *sessionFSAdapter) WriteFile(request *rpc.SessionFSWriteFileRequest) (*r mode = &m } if err := a.provider.WriteFile(request.Path, request.Content, mode); err != nil { + wire := toSessionFSError(err) + var changed *SessionFSWriteFailure + if errors.As(err, &changed) && changed != nil { + value := true + wire.WriteChanged = &value + } + return wire, nil + } + return nil, nil +} + +func (a *sessionFSAdapter) WriteFileBytes(request *rpc.SessionFSWriteFileBytesRequest) (*rpc.SessionFSError, error) { + provider, ok := a.provider.(SessionFSBinaryProvider) + if !ok { + return toSessionFSError(errors.New("binary writes are not supported")), nil + } + if len(request.Content) > maxSessionFSBinaryContentLength { + return toSessionFSError(errors.New("sessionFs.writeFileBytes content exceeds the binary write limit")), nil + } + content, err := base64.StdEncoding.Strict().DecodeString(request.Content) + if err != nil { + return toSessionFSError(err), nil + } + if base64.StdEncoding.EncodeToString(content) != request.Content { + return toSessionFSError(errors.New("invalid sessionFs.writeFileBytes base64 content")), nil + } + if len(content) > maxSessionFSBinaryBytes { + return toSessionFSError(errors.New("sessionFs.writeFileBytes content exceeds the binary write limit")), nil + } + var mode *int + if request.Mode != nil { + m := int(*request.Mode) + mode = &m + } + if err := provider.WriteFileBytes(request.Path, content, mode); err != nil { return toSessionFSError(err), nil } return nil, nil diff --git a/go/session_fs_provider_test.go b/go/session_fs_provider_test.go new file mode 100644 index 0000000000..e0b3d3aba0 --- /dev/null +++ b/go/session_fs_provider_test.go @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package copilot + +import ( + "errors" + "fmt" + "os" + "testing" + + "github.com/github/copilot-sdk/go/rpc" +) + +type failingWriteProvider struct { + SessionFSProvider + err error +} + +func (p failingWriteProvider) WriteFile(string, string, *int) error { return p.err } +func (p failingWriteProvider) AppendFile(string, string, *int) error { return p.err } + +func TestSessionFSWriteFailure(t *testing.T) { + for _, tc := range []struct { + name string + err error + wantChanged bool + wantCode rpc.SessionFSErrorCode + wantMessage string + }{ + {"rejected", errors.New("rejected"), false, rpc.SessionFSErrorCodeUNKNOWN, "rejected"}, + {"partial", &SessionFSWriteFailure{Err: errors.New("disk full")}, true, rpc.SessionFSErrorCodeUNKNOWN, "disk full"}, + {"wrapped", fmt.Errorf("write failed: %w", &SessionFSWriteFailure{Err: os.ErrNotExist}), true, rpc.SessionFSErrorCodeENOENT, "write failed: file does not exist"}, + {"nil cause", &SessionFSWriteFailure{}, true, rpc.SessionFSErrorCodeUNKNOWN, "session filesystem write failed after changing the target"}, + {"nil marker", (*SessionFSWriteFailure)(nil), false, rpc.SessionFSErrorCodeUNKNOWN, "session filesystem write failed after changing the target"}, + } { + t.Run(tc.name, func(t *testing.T) { + adapter := newSessionFSAdapter(failingWriteProvider{err: tc.err}) + wire, err := adapter.WriteFile(&rpc.SessionFSWriteFileRequest{Path: "/file", Content: "data"}) + if err != nil || wire == nil { + t.Fatalf("WriteFile = %v, %v; want wire error", wire, err) + } + if wire.Code != tc.wantCode { + t.Errorf("code = %v; want %v", wire.Code, tc.wantCode) + } + if wire.Message == nil || *wire.Message != tc.wantMessage { + t.Errorf("message = %v; want %q", wire.Message, tc.wantMessage) + } + gotChanged := wire.WriteChanged != nil && *wire.WriteChanged + if gotChanged != tc.wantChanged || !tc.wantChanged && wire.WriteChanged != nil { + t.Errorf("writeChanged = %v; want %v", wire.WriteChanged, tc.wantChanged) + } + }) + } +} + +func TestSessionFSWriteFailureDoesNotMarkAppend(t *testing.T) { + adapter := newSessionFSAdapter(failingWriteProvider{ + err: &SessionFSWriteFailure{Err: errors.New("append failed")}, + }) + wire, err := adapter.AppendFile(&rpc.SessionFSAppendFileRequest{Path: "/file", Content: "data"}) + if err != nil || wire == nil || wire.WriteChanged != nil { + t.Fatalf("AppendFile = %v, %v; want unmarked wire error", wire, err) + } +} diff --git a/go/session_retirement_test.go b/go/session_retirement_test.go new file mode 100644 index 0000000000..44c646d261 --- /dev/null +++ b/go/session_retirement_test.go @@ -0,0 +1,364 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. + +package copilot + +import ( + "context" + "fmt" + "strings" + "testing" + "time" +) + +func TestSession_RetirementClearsCallbackRoots(t *testing.T) { + session, cleanup := newTestSession() + t.Cleanup(cleanup) + session.On(func(SessionEvent) {}) + entry := session.handlers[0] + session.registerTools([]Tool{{Name: "tool", Handler: func(ToolInvocation) (ToolResult, error) { return ToolResult{}, nil }}}) + session.registerPermissionHandler(PermissionHandler.ApproveAll) + session.registerMCPAuthHandler(func(MCPAuthRequest, MCPAuthInvocation) (*MCPAuthResult, error) { return nil, nil }) + session.registerUserInputHandler(func(UserInputRequest, UserInputInvocation) (UserInputResponse, error) { + return UserInputResponse{}, nil + }) + session.registerExitPlanModeHandler(func(ExitPlanModeRequest, ExitPlanModeInvocation) (ExitPlanModeResult, error) { + return ExitPlanModeResult{}, nil + }) + session.registerAutoModeSwitchHandler(func(AutoModeSwitchRequest, AutoModeSwitchInvocation) (AutoModeSwitchResponse, error) { + return AutoModeSwitchResponseNo, nil + }) + hooks := &SessionHooks{OnSessionEnd: func(SessionEndHookInput, HookInvocation) (*SessionEndHookOutput, error) { + return nil, nil + }} + session.registerHooks(hooks) + transforms := map[string]SectionTransformFn{"instructions": func(value string) (string, error) { return value, nil }} + session.registerTransformCallbacks(transforms) + session.registerCommands([]CommandDefinition{{Name: "command", Handler: func(CommandContext) error { return nil }}}) + session.registerElicitationHandler(func(ElicitationContext) (ElicitationResult, error) { return ElicitationResult{}, nil }) + session.registerCanvasHandler(&recordingCanvasHandler{}) + session.registerBearerTokenProviders(map[string]BearerTokenProvider{ + "provider": func(ProviderTokenArgs) (string, error) { return "token", nil }, + }) + releases := 0 + session.setGitHubTokenProviderRegistrationRelease(func() { releases++ }) + + session.stopEventProcessing() + session.stopEventProcessing() + + for name, retained := range map[string]bool{ + "event registrations": len(session.handlers) != 0, + "retained event entry": entry.fn != nil, + "tools": session.toolHandlers != nil, + "permissions": session.permissionHandler != nil, + "MCP auth": session.mcpAuthHandler != nil, + "user input": session.userInputHandler != nil, + "exit plan mode": session.exitPlanModeHandler != nil, + "auto mode switch": session.autoModeSwitchHandler != nil, + "hooks": session.hooks != nil, + "transforms": session.transformCallbacks != nil, + "commands": session.commandHandlers != nil, + "elicitation": session.elicitationHandler != nil, + "canvas": session.canvasHandler != nil, + "bearer token": session.bearerTokenProviders != nil, + "provider release": session.releaseGitHubTokenProvider != nil, + } { + if retained { + t.Errorf("retired session retained %s", name) + } + } + if releases != 1 { + t.Errorf("provider releases = %d, want exactly one", releases) + } + if hooks.OnSessionEnd == nil || transforms["instructions"] == nil { + t.Fatal("retirement mutated caller-owned hook or transform configuration") + } +} + +func TestSession_RetirementRejectsCallbackRebinding(t *testing.T) { + session, cleanup := newTestSession() + t.Cleanup(cleanup) + session.stopEventProcessing() + unsubscribe := session.On(func(SessionEvent) {}) + if len(session.handlers) != 0 { + t.Fatal("retired session accepted a late event registration") + } + unsubscribe() + unsubscribe() + session.registerTools([]Tool{{Name: "late", Handler: func(ToolInvocation) (ToolResult, error) { return ToolResult{}, nil }}}) + if session.toolHandlers != nil { + t.Fatal("retired session retained late tool handlers") + } +} + +func TestSession_RetirementRejectsEmptyCallbackSuccess(t *testing.T) { + t.Run("hooks", func(t *testing.T) { + session, cleanup := newTestSession() + t.Cleanup(cleanup) + session.SessionID = "retirement-session" + client := &Client{sessions: map[string]*Session{session.SessionID: session}} + request := hooksInvokeRequest{SessionID: session.SessionID, Type: "preToolUse", Input: []byte("{}")} + if _, err := client.handleHooksInvoke(request); err != nil { + t.Fatalf("live session without hooks returned an error: %v", err) + } + session.stopEventProcessing() + _, err := client.handleHooksInvoke(request) + if err == nil || err.Code != -32603 || !strings.Contains(err.Message, "session closed") { + t.Fatalf("retired hook request error = %+v, want closed-session RPC error", err) + } + }) + t.Run("system message transforms", func(t *testing.T) { + session, cleanup := newTestSession() + t.Cleanup(cleanup) + sections := map[string]systemMessageTransformSection{"instructions": {Content: "keep this policy"}} + response, err := session.handleSystemMessageTransform(sections) + if err != nil || response.Sections["instructions"].Content != "keep this policy" { + t.Fatalf("live session without transforms changed content: %+v, %v", response, err) + } + session.stopEventProcessing() + response, err = session.handleSystemMessageTransform(sections) + if err == nil || !strings.Contains(err.Error(), "session closed") { + t.Fatalf("retired transform response = %+v, %v; want closed error", response, err) + } + }) +} + +func TestSession_RetirementRejectsModeRequestDefaults(t *testing.T) { + t.Run("exit plan mode", func(t *testing.T) { + session, cleanup := newTestSession() + t.Cleanup(cleanup) + session.stopEventProcessing() + response, err := session.handleExitPlanModeRequest(ExitPlanModeRequest{}) + if err == nil || !strings.Contains(err.Error(), "session closed") || response.Approved { + t.Fatalf("retired exit-plan-mode response = %+v, %v; want closed error without approval", response, err) + } + }) + t.Run("auto mode switch", func(t *testing.T) { + session, cleanup := newTestSession() + t.Cleanup(cleanup) + session.stopEventProcessing() + response, err := session.handleAutoModeSwitchRequest(AutoModeSwitchRequest{}) + if err == nil || !strings.Contains(err.Error(), "session closed") { + t.Fatalf("retired auto-mode-switch response = %+v, %v; want closed error", response, err) + } + }) +} + +func TestSession_SetToolsAcceptedAfterRetirementDoesNotRebind(t *testing.T) { + session, server := newSetToolsTestSession(t) + session.eventDone = make(chan struct{}) + session.eventCh = make(chan SessionEvent, 2) + processingDone := make(chan struct{}) + go func() { + defer close(processingDone) + session.processEvents() + }() + t.Cleanup(func() { + session.stopEventProcessing() + select { + case <-processingDone: + case <-time.After(2 * time.Second): + t.Error("event processing did not stop after retirement") + } + }) + result := make(chan error, 1) + go func() { + result <- session.SetTools(context.Background(), []Tool{{ + Name: "late", Handler: func(ToolInvocation) (ToolResult, error) { return ToolResult{}, nil }, + }}) + }() + request := server.expectRequest(t, "session.tools.set") + // Keep the transport alive so an accepted late ACK cannot mask local retirement. + session.stopEventProcessing() + server.respond(t, request.ID, map[string]any{}) + err := awaitSetTools(t, result) + if err == nil || !strings.Contains(err.Error(), "session closed") { + t.Fatalf("SetTools error = %v, want session closed", err) + } + + if _, ok := session.getToolHandler("late"); ok { + t.Fatal("accepted late replacement resurrected retired tool handlers") + } +} + +func TestSession_SendAndWaitPreservesTerminalResultBeforeRetirement(t *testing.T) { + for _, terminal := range []string{"idle", "error", "missing", "error-then-idle", "idle-then-error"} { + for iteration := range 32 { + t.Run(fmt.Sprintf("%s-%d", terminal, iteration), func(t *testing.T) { + session, server := newSetToolsTestSession(t) + session.eventDone = make(chan struct{}) + session.eventCh = make(chan SessionEvent, 2) + processingDone := make(chan struct{}) + go func() { + defer close(processingDone) + session.processEvents() + }() + t.Cleanup(func() { + session.stopEventProcessing() + select { + case <-processingDone: + case <-time.After(2 * time.Second): + t.Error("event processing did not stop after retirement") + } + }) + type response struct { + event *SessionEvent + err error + } + result := make(chan response, 1) + go func() { + event, err := session.SendAndWait(context.Background(), MessageOptions{Prompt: "finish"}) + result <- response{event: event, err: err} + }() + request := server.expectRequest(t, "session.send") + terminalObserved := make(chan struct{}, 2) + unsubscribe := session.On(func(event SessionEvent) { + switch event.Data.(type) { + case *SessionIdleData, *SessionErrorData: + terminalObserved <- struct{}{} + } + }) + defer unsubscribe() + switch terminal { + case "idle", "idle-then-error": + session.dispatchEvent(SessionEvent{Data: &AssistantMessageData{Content: "finished"}}) + session.dispatchEvent(SessionEvent{Data: &SessionIdleData{}}) + if terminal == "idle-then-error" { + session.dispatchEvent(SessionEvent{Data: &SessionErrorData{Message: "later provider failure"}}) + } + case "error", "error-then-idle": + session.dispatchEvent(SessionEvent{Data: &SessionErrorData{Message: "provider failed"}}) + if terminal == "error-then-idle" { + session.dispatchEvent(SessionEvent{Data: &SessionIdleData{}}) + } + } + if terminal != "missing" { + count := 1 + if terminal == "error-then-idle" || terminal == "idle-then-error" { + count = 2 + } + for range count { + select { + case <-terminalObserved: + case <-time.After(2 * time.Second): + t.Fatal("the wait did not receive its terminal event before retirement") + } + } + } + // Publish retirement before the send ACK so it can race the terminal result. + session.stopEventProcessing() + server.respond(t, request.ID, map[string]any{"messageId": "message-1"}) + select { + case response := <-result: + switch terminal { + case "idle", "idle-then-error": + if response.err != nil || response.event == nil { + t.Fatalf("completed response = %+v, %v; want assistant message", response.event, response.err) + } + message, ok := response.event.Data.(*AssistantMessageData) + if !ok || message.Content != "finished" { + t.Fatalf("assistant response = %+v, want finished", response.event.Data) + } + case "error", "error-then-idle": + if response.err == nil || response.err.Error() != "session error: provider failed" { + t.Fatalf("terminal error = %v, want provider failure", response.err) + } + case "missing": + if response.err == nil || response.err.Error() != "session closed before response completed" { + t.Fatalf("incomplete response error = %v, want session closure", response.err) + } + } + case <-time.After(2 * time.Second): + t.Fatal("SendAndWait did not observe the acknowledged send and retirement") + } + }) + } + } +} + +func TestSession_SendAndWaitPreservesTerminalResultWhenHandlerDisconnects(t *testing.T) { + for _, terminal := range []string{"idle", "error"} { + t.Run(terminal, func(t *testing.T) { + t.Parallel() + session, server := newSetToolsTestSession(t) + session.eventDone = make(chan struct{}) + session.eventCh = make(chan SessionEvent, 2) + processingDone := make(chan struct{}) + go func() { + defer close(processingDone) + session.processEvents() + }() + t.Cleanup(func() { + session.stopEventProcessing() + select { + case <-processingDone: + case <-time.After(2 * time.Second): + t.Error("event processing did not stop after retirement") + } + }) + + disconnected := make(chan error, 1) + session.On(func(event SessionEvent) { + switch event.Data.(type) { + case *SessionIdleData, *SessionErrorData: + disconnected <- session.Disconnect() + } + }) + lateCallback := make(chan struct{}, 1) + session.On(func(event SessionEvent) { + switch event.Data.(type) { + case *SessionIdleData, *SessionErrorData: + lateCallback <- struct{}{} + } + }) + type response struct { + event *SessionEvent + err error + } + result := make(chan response, 1) + ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) + defer cancel() + go func() { + event, err := session.SendAndWait(ctx, MessageOptions{Prompt: "finish"}) + result <- response{event: event, err: err} + }() + send := server.expectRequest(t, "session.send") + session.dispatchEvent(SessionEvent{Data: &AssistantMessageData{Content: "finished"}}) + if terminal == "idle" { + session.dispatchEvent(SessionEvent{Data: &SessionIdleData{}}) + } else { + session.dispatchEvent(SessionEvent{Data: &SessionErrorData{Message: "provider failed"}}) + } + detach := server.expectRequest(t, "session.detach") + server.respond(t, detach.ID, map[string]any{"success": true}) + select { + case err := <-disconnected: + if err != nil { + t.Fatal("disconnect:", err) + } + case <-ctx.Done(): + t.Fatal("terminal handler did not disconnect") + } + select { + case <-lateCallback: + t.Fatal("retirement admitted a later user callback from the same event snapshot") + default: + } + // Acknowledge only after the earlier handler has retired the session. + server.respond(t, send.ID, map[string]any{"messageId": "message-1"}) + select { + case response := <-result: + if terminal == "error" { + if response.err == nil || response.err.Error() != "session error: provider failed" { + t.Fatalf("terminal error = %v, want provider failure", response.err) + } + } else if response.err != nil || response.event == nil { + t.Fatalf("completed response = %+v, %v; want assistant message", response.event, response.err) + } else if message, ok := response.event.Data.(*AssistantMessageData); !ok || message.Content != "finished" { + t.Fatalf("assistant response = %+v, want finished", response.event.Data) + } + case <-ctx.Done(): + t.Fatal("SendAndWait did not preserve the terminal result") + } + }) + } +} diff --git a/go/session_test.go b/go/session_test.go index 60e51308e6..38a365fbd6 100644 --- a/go/session_test.go +++ b/go/session_test.go @@ -4,6 +4,7 @@ import ( "bufio" "context" "encoding/json" + "errors" "fmt" "io" "strconv" @@ -21,7 +22,7 @@ import ( // Returns a cleanup function that stops the consumer. func newTestSession() (*Session, func()) { s := &Session{ - handlers: make([]sessionHandler, 0), + handlers: make([]*sessionHandler, 0), commandHandlers: make(map[string]CommandHandler), eventCh: make(chan SessionEvent, 128), eventDone: make(chan struct{}), @@ -70,6 +71,68 @@ func TestExternalToolCompletedCancelsBlockedHandler(t *testing.T) { } } +func TestToolInvocationNamesTheSubAgentAndRequestBehindTheCall(t *testing.T) { + session, cleanup := newTestSession() + defer cleanup() + + invocations := make(chan ToolInvocation, 3) + session.registerTools([]Tool{{ + Name: "calc", + Handler: func(invocation ToolInvocation) (ToolResult, error) { + invocations <- invocation + <-invocation.TraceContext.Done() + return ToolResult{}, invocation.TraceContext.Err() + }, + }}) + + session.dispatchEvent(SessionEvent{ + AgentID: ptr("task-agent-1"), + Data: &ExternalToolRequestedData{ + RequestID: "req-sub-agent", + SessionID: "session-1", + ToolCallID: "tc-sub-agent", + ToolName: "calc", + }, + }) + defer session.dispatchEvent(SessionEvent{Data: &ExternalToolCompletedData{RequestID: "req-sub-agent"}}) + session.dispatchEvent(SessionEvent{Data: &ExternalToolRequestedData{ + RequestID: "req-root", + SessionID: "session-1", + ToolCallID: "tc-root", + ToolName: "calc", + }}) + defer session.dispatchEvent(SessionEvent{Data: &ExternalToolCompletedData{RequestID: "req-root"}}) + session.dispatchEvent(SessionEvent{ + AgentID: ptr(""), + Data: &ExternalToolRequestedData{ + RequestID: "req-blank-agent", + SessionID: "session-1", + ToolCallID: "tc-blank-agent", + ToolName: "calc", + }, + }) + defer session.dispatchEvent(SessionEvent{Data: &ExternalToolCompletedData{RequestID: "req-blank-agent"}}) + + got := make(map[string]ToolInvocation) + for range 3 { + select { + case invocation := <-invocations: + got[invocation.ToolCallID] = invocation + case <-time.After(time.Second): + t.Fatal("tool handler did not start") + } + } + if sub := got["tc-sub-agent"]; sub.AgentID != "task-agent-1" || sub.RequestID != "req-sub-agent" { + t.Fatalf("sub-agent call: got AgentID %q RequestID %q", sub.AgentID, sub.RequestID) + } + if root := got["tc-root"]; root.AgentID != "" || root.RequestID != "req-root" { + t.Fatalf("root call: got AgentID %q RequestID %q", root.AgentID, root.RequestID) + } + if blank := got["tc-blank-agent"]; blank.AgentID != "" || blank.RequestID != "req-blank-agent" { + t.Fatalf("blank-agent call: got AgentID %q RequestID %q", blank.AgentID, blank.RequestID) + } +} + func TestDispatchEventReturnsAfterEventProcessingStops(t *testing.T) { session := &Session{ eventCh: make(chan SessionEvent), @@ -184,6 +247,321 @@ func TestSession_SetAutoTierSendsExplicitNull(t *testing.T) { } } +func TestSession_SetToolsSendsPayloadAndInstallsHandlers(t *testing.T) { + session, server := newSetToolsTestSession(t) + handler := func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{TextResultForLLM: "handled"}, nil + } + tools := []Tool{ + { + Name: "lookup_fruit", + Parameters: map[string]any{"type": "object"}, + OverridesBuiltInTool: true, + SkipPermission: true, + IsTerminal: true, + Defer: ToolDeferNever, + Metadata: map[string]any{"x-test": "metadata"}, + Handler: handler, + }, + { + Name: "declaration_only", + Description: "No handler", + }, + } + + errCh := make(chan error, 1) + go func() { + errCh <- session.SetTools(context.Background(), tools) + }() + + request := server.expectRequest(t, "session.tools.set") + if request.Params["sessionId"] != "session-1" { + t.Fatalf("sessionId = %v, want session-1", request.Params["sessionId"]) + } + wireTools, ok := request.Params["tools"].([]any) + if !ok || len(wireTools) != 2 { + t.Fatalf("tools = %#v, want two tools", request.Params["tools"]) + } + first, ok := wireTools[0].(map[string]any) + if !ok { + t.Fatalf("first tool = %#v, want object", wireTools[0]) + } + if first["name"] != "lookup_fruit" { + t.Fatalf("name = %v, want lookup_fruit", first["name"]) + } + if description, ok := first["description"].(string); !ok || description != "" { + t.Fatalf("description = %#v, want empty string", first["description"]) + } + for _, key := range []string{"overridesBuiltInTool", "skipPermission", "isTerminal"} { + if first[key] != true { + t.Fatalf("%s = %v, want true", key, first[key]) + } + } + if first["defer"] != "never" { + t.Fatalf("defer = %v, want never", first["defer"]) + } + if _, ok := first["parameters"].(map[string]any); !ok { + t.Fatalf("parameters = %#v, want object", first["parameters"]) + } + if _, ok := first["metadata"].(map[string]any); !ok { + t.Fatalf("metadata = %#v, want object", first["metadata"]) + } + + server.respond(t, request.ID, map[string]any{}) + if err := awaitSetTools(t, errCh); err != nil { + t.Fatalf("SetTools failed: %v", err) + } + if got, ok := session.getToolHandler("lookup_fruit"); !ok || got == nil { + t.Fatal("lookup_fruit handler was not installed") + } + if _, ok := session.getToolHandler("declaration_only"); ok { + t.Fatal("declaration-only tool installed a handler") + } +} + +func TestSession_SetToolsSwitchesHandlersAfterAccepted(t *testing.T) { + session, server := newSetToolsTestSession(t) + oldHandler := func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{TextResultForLLM: "old"}, nil + } + newHandler := func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{TextResultForLLM: "new"}, nil + } + session.registerTools([]Tool{{Name: "lookup", Handler: oldHandler}}) + + errCh := make(chan error, 1) + go func() { + errCh <- session.SetTools(context.Background(), []Tool{{Name: "lookup", Handler: newHandler}}) + }() + request := server.expectRequest(t, "session.tools.set") + + handler, ok := session.getToolHandler("lookup") + if !ok { + t.Fatal("lookup handler missing while replacement RPC is pending") + } + result, err := handler(ToolInvocation{}) + if err != nil || result.TextResultForLLM != "old" { + t.Fatalf("pending replacement used %q, %v; want old handler", result.TextResultForLLM, err) + } + + server.respond(t, request.ID, map[string]any{}) + if err := awaitSetTools(t, errCh); err != nil { + t.Fatalf("SetTools failed: %v", err) + } + handler, ok = session.getToolHandler("lookup") + if !ok { + t.Fatal("lookup handler missing after accepted replacement") + } + result, err = handler(ToolInvocation{}) + if err != nil || result.TextResultForLLM != "new" { + t.Fatalf("accepted replacement used %q, %v; want new handler", result.TextResultForLLM, err) + } +} + +func TestSession_SetToolsRunningCallsFinishOnOriginalHandler(t *testing.T) { + session, server := newSetToolsTestSession(t) + started := make(chan struct{}) + release := make(chan struct{}) + oldHandler := func(_ ToolInvocation) (ToolResult, error) { + close(started) + <-release + return ToolResult{TextResultForLLM: "old"}, nil + } + newHandler := func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{TextResultForLLM: "new"}, nil + } + session.registerTools([]Tool{{Name: "lookup", Handler: oldHandler}}) + session.startExternalTool(&ExternalToolRequestedData{ + RequestID: "request-1", + SessionID: "session-1", + ToolCallID: "tool-call-1", + ToolName: "lookup", + }, "") + select { + case <-started: + case <-time.After(2 * time.Second): + t.Fatal("tool handler did not start") + } + + errCh := make(chan error, 1) + go func() { + errCh <- session.SetTools(context.Background(), []Tool{{Name: "lookup", Handler: newHandler}}) + }() + request := server.expectRequest(t, "session.tools.set") + server.respond(t, request.ID, map[string]any{}) + if err := awaitSetTools(t, errCh); err != nil { + t.Fatalf("SetTools failed: %v", err) + } + close(release) + + completion := server.expectRequest(t, "session.tools.handlePendingToolCall") + result, ok := completion.Params["result"].(map[string]any) + if !ok { + t.Fatalf("completion result = %#v, want object", completion.Params["result"]) + } + if result["textResultForLlm"] != "old" { + t.Fatalf("completion result = %#v, want old handler result", result) + } + server.respond(t, completion.ID, map[string]any{}) +} + +func TestSession_SetToolsRejectionLeavesHandlersUnchanged(t *testing.T) { + session, server := newSetToolsTestSession(t) + oldHandler := func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{TextResultForLLM: "old"}, nil + } + newHandler := func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{TextResultForLLM: "new"}, nil + } + session.registerTools([]Tool{{Name: "lookup", Handler: oldHandler}}) + + errCh := make(chan error, 1) + go func() { + errCh <- session.SetTools(context.Background(), []Tool{{Name: "lookup", Handler: newHandler}}) + }() + request := server.expectRequest(t, "session.tools.set") + server.respondError(t, request.ID, -32602, "invalid tool name") + if err := awaitSetTools(t, errCh); err == nil { + t.Fatal("SetTools succeeded; want rejection error") + } + + handler, ok := session.getToolHandler("lookup") + if !ok { + t.Fatal("lookup handler missing after rejected replacement") + } + result, err := handler(ToolInvocation{}) + if err != nil || result.TextResultForLLM != "old" { + t.Fatalf("rejected replacement used %q, %v; want old handler", result.TextResultForLLM, err) + } +} + +func TestSession_SetToolsEmptySetRemovesHandlers(t *testing.T) { + session, server := newSetToolsTestSession(t) + session.registerTools([]Tool{{Name: "lookup", Handler: func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{TextResultForLLM: "old"}, nil + }}}) + + errCh := make(chan error, 1) + go func() { + errCh <- session.SetTools(context.Background(), []Tool{}) + }() + request := server.expectRequest(t, "session.tools.set") + wireTools, ok := request.Params["tools"].([]any) + if !ok || len(wireTools) != 0 { + t.Fatalf("tools = %#v, want empty list", request.Params["tools"]) + } + server.respond(t, request.ID, map[string]any{}) + if err := awaitSetTools(t, errCh); err != nil { + t.Fatalf("SetTools failed: %v", err) + } + if _, ok := session.getToolHandler("lookup"); ok { + t.Fatal("handler remained after empty replacement") + } +} + +func TestSession_SetToolsConcurrentCallsAreSerialized(t *testing.T) { + session, server := newSetToolsTestSession(t) + + firstErr := make(chan error, 1) + go func() { + firstErr <- session.SetTools(context.Background(), []Tool{{Name: "first", Handler: func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{}, nil + }}}) + }() + first := server.expectRequest(t, "session.tools.set") + + secondErr := make(chan error, 1) + go func() { + secondErr <- session.SetTools(context.Background(), []Tool{{Name: "second", Handler: func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{}, nil + }}}) + }() + + server.expectNoRequest(t) + server.respondError(t, first.ID, -32602, "first rejected") + if err := awaitSetTools(t, firstErr); err == nil { + t.Fatal("first SetTools succeeded; want rejection error") + } + + second := server.expectRequest(t, "session.tools.set") + server.respond(t, second.ID, map[string]any{}) + if err := awaitSetTools(t, secondErr); err != nil { + t.Fatalf("second SetTools failed: %v", err) + } + if _, ok := session.getToolHandler("first"); ok { + t.Fatal("first handler installed after rejected replacement") + } + if _, ok := session.getToolHandler("second"); !ok { + t.Fatal("second handler was not installed after earlier rejection") + } +} + +func TestSession_SetToolsCancelledAfterSendStillInstallsAcceptedHandlers(t *testing.T) { + session, server := newSetToolsTestSession(t) + oldHandler := func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{TextResultForLLM: "old"}, nil + } + newHandler := func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{TextResultForLLM: "new"}, nil + } + session.registerTools([]Tool{{Name: "lookup", Handler: oldHandler}}) + + ctx, cancel := context.WithCancel(context.Background()) + errCh := make(chan error, 1) + go func() { + errCh <- session.SetTools(ctx, []Tool{{Name: "lookup", Handler: newHandler}}) + }() + request := server.expectRequest(t, "session.tools.set") + cancel() + if err := awaitSetTools(t, errCh); !errors.Is(err, context.Canceled) { + t.Fatalf("SetTools error = %v, want context.Canceled", err) + } + handler, ok := session.getToolHandler("lookup") + if !ok { + t.Fatal("lookup handler missing before accepted replacement resolves") + } + result, err := handler(ToolInvocation{}) + if err != nil || result.TextResultForLLM != "old" { + t.Fatalf("cancelled pending replacement used %q, %v; want old handler", result.TextResultForLLM, err) + } + + server.respond(t, request.ID, map[string]any{}) + waitForToolHandlerResult(t, session, "lookup", "new") +} + +func TestSession_SetToolsAlreadyCancelledWhileBlockedDoesNotSend(t *testing.T) { + session, server := newSetToolsTestSession(t) + firstErr := make(chan error, 1) + go func() { + firstErr <- session.SetTools(context.Background(), []Tool{{Name: "first", Handler: func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{}, nil + }}}) + }() + first := server.expectRequest(t, "session.tools.set") + + ctx, cancel := context.WithCancel(context.Background()) + secondErr := make(chan error, 1) + go func() { + secondErr <- session.SetTools(ctx, []Tool{{Name: "second", Handler: func(_ ToolInvocation) (ToolResult, error) { + return ToolResult{}, nil + }}}) + }() + cancel() + if err := awaitSetTools(t, secondErr); !errors.Is(err, context.Canceled) { + t.Fatalf("blocked SetTools error = %v, want context.Canceled", err) + } + server.expectNoRequest(t) + + server.respond(t, first.ID, map[string]any{}) + if err := awaitSetTools(t, firstErr); err != nil { + t.Fatalf("first SetTools failed: %v", err) + } + server.expectNoRequest(t) + if _, ok := session.getToolHandler("second"); ok { + t.Fatal("cancelled blocked replacement installed a handler") + } +} + func TestSession_MCPAuthRequestSendsHostToken(t *testing.T) { stdinR, stdinW := io.Pipe() stdoutR, stdoutW := io.Pipe() @@ -521,6 +899,169 @@ func readTestJSONRPCFrame(r io.Reader) ([]byte, error) { return data, err } +type setToolsJSONRPCRequest struct { + ID json.RawMessage `json:"id"` + Method string `json:"method"` + Params map[string]any `json:"params"` +} + +type setToolsTestServer struct { + stdinR *io.PipeReader + stdinW *io.PipeWriter + stdoutR *io.PipeReader + stdoutW *io.PipeWriter + client *jsonrpc2.Client + mu sync.Mutex + request chan setToolsJSONRPCRequest + errs chan error +} + +func newSetToolsTestSession(t *testing.T) (*Session, *setToolsTestServer) { + t.Helper() + + stdinR, stdinW := io.Pipe() + stdoutR, stdoutW := io.Pipe() + client := jsonrpc2.NewClient(stdinW, stdoutR) + client.Start() + + server := &setToolsTestServer{ + stdinR: stdinR, + stdinW: stdinW, + stdoutR: stdoutR, + stdoutW: stdoutW, + client: client, + request: make(chan setToolsJSONRPCRequest, 16), + errs: make(chan error, 1), + } + go server.readRequests() + t.Cleanup(func() { + client.Stop() + stdinR.Close() + stdinW.Close() + stdoutR.Close() + stdoutW.Close() + }) + + session := &Session{ + SessionID: "session-1", + client: client, + toolHandlers: make(map[string]ToolHandler), + RPC: rpc.NewSessionRPC(client, "session-1"), + } + return session, server +} + +func (s *setToolsTestServer) readRequests() { + for { + frame, err := readTestJSONRPCFrame(s.stdinR) + if err != nil { + return + } + + var request setToolsJSONRPCRequest + if err := json.Unmarshal(frame, &request); err != nil { + select { + case s.errs <- err: + default: + } + return + } + s.request <- request + } +} + +func (s *setToolsTestServer) expectRequest(t *testing.T, method string) setToolsJSONRPCRequest { + t.Helper() + select { + case request := <-s.request: + if request.Method != method { + t.Fatalf("method = %s, want %s", request.Method, method) + } + return request + case err := <-s.errs: + t.Fatal(err) + case <-time.After(2 * time.Second): + t.Fatalf("timed out waiting for %s request", method) + } + return setToolsJSONRPCRequest{} +} + +func (s *setToolsTestServer) expectNoRequest(t *testing.T) { + t.Helper() + select { + case request := <-s.request: + t.Fatalf("unexpected request while replacement should be blocked: %s", request.Method) + case err := <-s.errs: + t.Fatal(err) + case <-time.After(100 * time.Millisecond): + } +} + +func (s *setToolsTestServer) respond(t *testing.T, id json.RawMessage, result any) { + t.Helper() + s.writeResponse(t, map[string]any{ + "jsonrpc": "2.0", + "id": id, + "result": result, + }) +} + +func (s *setToolsTestServer) respondError(t *testing.T, id json.RawMessage, code int, message string) { + t.Helper() + s.writeResponse(t, map[string]any{ + "jsonrpc": "2.0", + "id": id, + "error": map[string]any{ + "code": code, + "message": message, + }, + }) +} + +func (s *setToolsTestServer) writeResponse(t *testing.T, response map[string]any) { + t.Helper() + data, err := json.Marshal(response) + if err != nil { + t.Fatal(err) + } + s.mu.Lock() + defer s.mu.Unlock() + if _, err := fmt.Fprintf(s.stdoutW, "Content-Length: %d\r\n\r\n%s", len(data), data); err != nil { + t.Fatal(err) + } +} + +func awaitSetTools(t *testing.T, errCh <-chan error) error { + t.Helper() + select { + case err := <-errCh: + return err + case <-time.After(2 * time.Second): + t.Fatal("timed out waiting for SetTools") + } + return nil +} + +func waitForToolHandlerResult(t *testing.T, session *Session, name, want string) { + t.Helper() + deadline := time.After(2 * time.Second) + for { + handler, ok := session.getToolHandler(name) + if ok { + result, err := handler(ToolInvocation{}) + if err == nil && result.TextResultForLLM == want { + return + } + } + select { + case <-deadline: + t.Fatalf("timed out waiting for %s handler to return %q", name, want) + default: + time.Sleep(time.Millisecond) + } + } +} + func TestSession_SendAndWaitSkipsSubagentAndAutopilotContinuationIdle(t *testing.T) { t.Run("with root reply", func(t *testing.T) { checkSendAndWaitSkipsSubagentAndAutopilotContinuationIdle(t, true) @@ -585,7 +1126,7 @@ func checkSendAndWaitSkipsSubagentAndAutopilotContinuationIdle(t *testing.T, roo SessionID: "session-1", client: client, RPC: rpc.NewSessionRPC(client, "session-1"), - handlers: make([]sessionHandler, 0), + handlers: make([]*sessionHandler, 0), eventCh: make(chan SessionEvent, 8), eventDone: make(chan struct{}), } diff --git a/go/skill_provider_test.go b/go/skill_provider_test.go new file mode 100644 index 0000000000..957b99aa8f --- /dev/null +++ b/go/skill_provider_test.go @@ -0,0 +1,700 @@ +package copilot + +import ( + "context" + "encoding/json" + "errors" + "log" + "strings" + "sync" + "testing" + + "github.com/github/copilot-sdk/go/internal/jsonrpc2" + "github.com/github/copilot-sdk/go/rpc" +) + +type testSkillProvider struct { + mu sync.Mutex + skills []rpc.SkillProviderDescriptor + markdown map[string]string + listErr error + readErr error + calls []string +} + +func (p *testSkillProvider) ListSkills(context.Context) ([]rpc.SkillProviderDescriptor, error) { + p.mu.Lock() + defer p.mu.Unlock() + p.calls = append(p.calls, "list") + if p.listErr != nil { + return nil, p.listErr + } + return p.skills, nil +} + +func (p *testSkillProvider) ReadSkill(_ context.Context, name string) (string, error) { + p.mu.Lock() + defer p.mu.Unlock() + p.calls = append(p.calls, "read:"+name) + if p.readErr != nil { + return "", p.readErr + } + markdown, ok := p.markdown[name] + if !ok { + return "", ErrSkillNotFound + } + return markdown, nil +} + +func (p *testSkillProvider) snapshotCalls() []string { + p.mu.Lock() + defer p.mu.Unlock() + return append([]string(nil), p.calls...) +} + +var reviewSkillDescriptor = rpc.SkillProviderDescriptor{ + Name: "review", + Description: "Reviews code", +} + +func newTestSkillProvider() *testSkillProvider { + return &testSkillProvider{ + skills: []rpc.SkillProviderDescriptor{reviewSkillDescriptor}, + markdown: map[string]string{"review": "Review carefully."}, + } +} + +func TestSkillProviderSessionPayloads(t *testing.T) { + t.Run("omits flag without provider and sends flag with provider", 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, 2) + resumeParams := 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 + }) + server.SetRequestHandler("session.resume", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + resumeParams <- append(json.RawMessage(nil), params...) + sessionID := sessionIDFromParams(t, params) + return []byte(`{"sessionId":"` + sessionID + `","workspacePath":"/workspace"}`), nil + }) + + if _, err := client.CreateSession(t.Context(), &SessionConfig{}); err != nil { + t.Fatalf("CreateSession without provider failed: %v", err) + } + if _, err := client.ResumeSession(t.Context(), "resume-without-provider", &ResumeSessionConfig{}); err != nil { + t.Fatalf("ResumeSession without provider failed: %v", err) + } + provider := newTestSkillProvider() + if _, err := client.CreateSession(t.Context(), &SessionConfig{SkillProvider: provider}); err != nil { + t.Fatalf("CreateSession with provider failed: %v", err) + } + if _, err := client.ResumeSession(t.Context(), "resume-with-provider", &ResumeSessionConfig{SkillProvider: provider}); err != nil { + t.Fatalf("ResumeSession with provider failed: %v", err) + } + + assertSkillProviderFlag(t, <-createParams, false) + assertSkillProviderFlag(t, <-resumeParams, false) + assertSkillProviderFlag(t, <-createParams, true) + assertSkillProviderFlag(t, <-resumeParams, true) + if got := provider.snapshotCalls(); len(got) != 0 { + t.Fatalf("provider was called during open: %v", got) + } + }) + + t.Run("keeps empty mode enableSkills default with provider", 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), + options: ClientOptions{Mode: ModeEmpty}, + } + + 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 + `"}`), nil + }) + server.SetRequestHandler("session.options.update", func(json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + return []byte(`{}`), nil + }) + + if _, err := client.CreateSession(t.Context(), &SessionConfig{ + AvailableTools: []string{}, + SkillProvider: newTestSkillProvider(), + }); err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + var payload map[string]any + if err := json.Unmarshal(<-createParams, &payload); err != nil { + t.Fatal(err) + } + if payload["enableSkills"] != false { + t.Fatalf("enableSkills = %v, want false", payload["enableSkills"]) + } + if payload["hasSkillProvider"] != true { + t.Fatalf("hasSkillProvider = %v, want true", payload["hasSkillProvider"]) + } + }) +} + +func TestSkillProviderServesEarlyCallbacksDuringOpen(t *testing.T) { + for _, method := range []string{"session.create", "session.resume"} { + t.Run(method, 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), + } + client.setupNotificationHandler() + + earlyResult := make(chan rpc.SkillProviderListResult, 1) + server.SetRequestHandler(method, func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + sessionID := sessionIDFromParams(t, params) + raw, err := server.Request(t.Context(), "skillProvider.list", map[string]any{"sessionId": sessionID}) + if err != nil { + if rpcErr, ok := err.(*jsonrpc2.Error); ok { + return nil, rpcErr + } + return nil, &jsonrpc2.Error{Code: -32603, Message: err.Error()} + } + var result rpc.SkillProviderListResult + if err := json.Unmarshal(raw, &result); err != nil { + return nil, &jsonrpc2.Error{Code: -32603, Message: err.Error()} + } + earlyResult <- result + return []byte(`{"sessionId":"` + sessionID + `"}`), nil + }) + + provider := newTestSkillProvider() + if method == "session.create" { + if _, err := client.CreateSession(t.Context(), &SessionConfig{SkillProvider: provider}); err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + } else if _, err := client.ResumeSession(t.Context(), "early-resume", &ResumeSessionConfig{SkillProvider: provider}); err != nil { + t.Fatalf("ResumeSession failed: %v", err) + } + + got := <-earlyResult + if len(got.Skills) != 1 || got.Skills[0].Name != "review" { + t.Fatalf("early list result = %+v", got) + } + }) + } +} + +func TestSkillProviderRejectsCloudBeforeConnect(t *testing.T) { + provider := newTestSkillProvider() + client := &Client{} + + _, err := client.CreateSession(t.Context(), &SessionConfig{ + Cloud: &CloudSessionOptions{}, + SkillProvider: provider, + }) + + if err == nil || err.Error() != "Skill providers are not supported for cloud sessions." { + t.Fatalf("CreateSession error = %v", err) + } + if client.client != nil { + t.Fatal("client connected before rejecting cloud skill provider") + } + if got := provider.snapshotCalls(); len(got) != 0 { + t.Fatalf("provider was called: %v", got) + } +} + +func TestSkillProviderDispatch(t *testing.T) { + rpcClient, server, _ := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + sessions: make(map[string]*Session), + } + client.setupNotificationHandler() + + provider := &testSkillProvider{ + skills: []rpc.SkillProviderDescriptor{ + reviewSkillDescriptor, + { + Name: "deploy", + Description: "Deploys the service", + UserInvocable: Bool(false), + DisableModelInvocation: Bool(true), + ArgumentHint: String("[environment]"), + }, + }, + markdown: map[string]string{"deploy": "# deploy"}, + } + session := newSession("dispatch-session", rpcClient, "", false) + session.registerSkillProvider(provider) + client.sessions[session.SessionID] = session + + listRaw, rpcErr := server.Request(t.Context(), "skillProvider.list", map[string]any{"sessionId": session.SessionID}) + if rpcErr != nil { + t.Fatalf("skillProvider.list failed: %v", rpcErr) + } + var listResult rpc.SkillProviderListResult + if err := json.Unmarshal(listRaw, &listResult); err != nil { + t.Fatal(err) + } + if len(listResult.Skills) != 2 || listResult.Skills[1].Name != "deploy" { + t.Fatalf("list result = %+v", listResult) + } + + readRaw, rpcErr := server.Request(t.Context(), "skillProvider.read", map[string]any{ + "sessionId": session.SessionID, + "name": "deploy", + }) + if rpcErr != nil { + t.Fatalf("skillProvider.read failed: %v", rpcErr) + } + var readResult rpc.SkillProviderReadResult + if err := json.Unmarshal(readRaw, &readResult); err != nil { + t.Fatal(err) + } + if readResult.Markdown == nil { + t.Fatal("markdown = nil, want # deploy") + } + if *readResult.Markdown != "# deploy" { + t.Fatalf("markdown = %q, want # deploy", *readResult.Markdown) + } + if got := provider.snapshotCalls(); strings.Join(got, ",") != "list,read:deploy" { + t.Fatalf("provider calls = %v", got) + } +} + +func TestSkillProviderOmitsUnsetOptionalDescriptorFields(t *testing.T) { + data, err := json.Marshal(rpc.SkillProviderListResult{ + Skills: []rpc.SkillProviderDescriptor{{Name: "review", Description: "Reviews code"}}, + }) + if err != nil { + t.Fatal(err) + } + var decoded map[string][]map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatal(err) + } + skill := decoded["skills"][0] + for _, key := range []string{"argumentHint", "userInvocable", "disableModelInvocation"} { + if _, ok := skill[key]; ok { + t.Fatalf("%s should be omitted when unset: %s", key, data) + } + } +} + +func TestSkillProviderEmptyListBecomesEmptyArray(t *testing.T) { + rpcClient, server, _ := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + sessions: make(map[string]*Session), + } + client.setupNotificationHandler() + session := newSession("empty-list-session", rpcClient, "", false) + session.registerSkillProvider(&testSkillProvider{}) + client.sessions[session.SessionID] = session + + raw, rpcErr := server.Request(t.Context(), "skillProvider.list", map[string]any{"sessionId": session.SessionID}) + if rpcErr != nil { + t.Fatalf("skillProvider.list failed: %v", rpcErr) + } + if string(raw) != `{"skills":[]}` { + t.Fatalf("list response = %s, want empty skills array", raw) + } +} + +func TestSkillProviderErrorEnvelopes(t *testing.T) { + t.Run("read not found returns null markdown", func(t *testing.T) { + _, server, sessionID := skillProviderErrorHarness(t, newTestSkillProvider()) + raw, rpcErr := server.Request(t.Context(), "skillProvider.read", map[string]any{ + "sessionId": sessionID, + "name": "missing", + }) + if rpcErr != nil { + t.Fatalf("skillProvider.read failed: %v", rpcErr) + } + if string(raw) != `{"markdown":null}` { + t.Fatalf("read response = %s, want null markdown", raw) + } + var result rpc.SkillProviderReadResult + if err := json.Unmarshal(raw, &result); err != nil { + t.Fatal(err) + } + if result.Markdown != nil { + t.Fatalf("markdown = %q, want nil", *result.Markdown) + } + }) + + for _, method := range []string{"skillProvider.list", "skillProvider.read"} { + t.Run(method+" provider failure", func(t *testing.T) { + output := captureSkillProviderLog(t) + secret := errors.New("db-password-in-error") + provider := newTestSkillProvider() + operation := "listSkills" + if method == "skillProvider.list" { + provider.listErr = secret + } else { + provider.readErr = secret + operation = "readSkill" + } + _, server, sessionID := skillProviderErrorHarness(t, provider) + params := map[string]any{"sessionId": sessionID} + wantMessage := "Skill provider " + operation + " failed" + if method == "skillProvider.read" { + params["name"] = "review" + } + + rpcErr := requestSkillProviderError(t, server, method, params) + if rpcErr.Code != -32603 || rpcErr.Message != wantMessage { + t.Fatalf("error = %+v, want generic message %q", rpcErr, wantMessage) + } + if rpcErr.Data != nil { + t.Fatalf("generic provider error data = %s, want omitted", rpcErr.Data) + } + if strings.Contains(rpcErr.Message, secret.Error()) { + t.Fatalf("provider error leaked secret: %q", rpcErr.Message) + } + wantLog := "skill provider " + operation + " failed: session_id=" + sessionID + " error=" + secret.Error() + if got := output.text(); !strings.Contains(got, wantLog) { + t.Fatalf("log = %q, want it to contain %q", got, wantLog) + } + }) + } + + t.Run("not-found classification panic", func(t *testing.T) { + captureSkillProviderLog(t) + provider := newTestSkillProvider() + provider.readErr = panickyIsError{} + _, server, sessionID := skillProviderErrorHarness(t, provider) + + rpcErr := requestSkillProviderError(t, server, "skillProvider.read", map[string]any{ + "sessionId": sessionID, + "name": "review", + }) + if rpcErr.Code != -32603 || rpcErr.Message != "Skill provider readSkill failed" || rpcErr.Data != nil { + t.Fatalf("error = %+v, want generic readSkill failure", rpcErr) + } + }) + + t.Run("unknown session", func(t *testing.T) { + _, server, _ := skillProviderErrorHarness(t, newTestSkillProvider()) + rpcErr := requestSkillProviderError(t, server, "skillProvider.list", map[string]any{"sessionId": "missing"}) + assertSkillProviderError(t, rpcErr, "No skill provider for session: missing") + }) + + for _, method := range []string{"skillProvider.list", "skillProvider.read"} { + t.Run(method+" provider panic", func(t *testing.T) { + output := captureSkillProviderLog(t) + _, server, sessionID := skillProviderErrorHarness(t, panickingSkillProvider{}) + params := map[string]any{"sessionId": sessionID} + wantMessage := "Skill provider listSkills failed" + if method == "skillProvider.read" { + params["name"] = "review" + wantMessage = "Skill provider readSkill failed" + } + + rpcErr := requestSkillProviderError(t, server, method, params) + if rpcErr.Code != -32603 || rpcErr.Message != wantMessage || rpcErr.Data != nil { + t.Fatalf("error = %+v, want generic message %q without data", rpcErr, wantMessage) + } + if got := output.text(); !strings.Contains(got, "panic=db-password-in-panic") { + t.Fatalf("log = %q, want the recovered panic value", got) + } + }) + } + + t.Run("no provider", 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), + } + client.setupNotificationHandler() + session := newSession("no-provider-session", rpcClient, "", false) + client.sessions[session.SessionID] = session + + rpcErr := requestSkillProviderError(t, server, "skillProvider.read", map[string]any{ + "sessionId": session.SessionID, + "name": "review", + }) + assertSkillProviderError(t, rpcErr, "No skill provider for session: no-provider-session") + }) +} + +func TestSkillProviderTeardown(t *testing.T) { + t.Run("disconnect clears provider", func(t *testing.T) { + client, server, provider, session := newSkillProviderOpenSession(t, "disconnect-session") + + if err := session.Disconnect(); err != nil { + t.Fatalf("Disconnect failed: %v", err) + } + rpcErr := requestSkillProviderError(t, server, "skillProvider.list", map[string]any{"sessionId": session.SessionID}) + assertSkillProviderError(t, rpcErr, "No skill provider for session: disconnect-session") + if got := provider.snapshotCalls(); len(got) != 0 { + t.Fatalf("provider was called after disconnect: %v", got) + } + _ = client + }) + + t.Run("delete clears provider", func(t *testing.T) { + client, server, provider, session := newSkillProviderOpenSession(t, "delete-session") + server.SetRequestHandler("session.delete", func(json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + return []byte(`{"success":true}`), nil + }) + + if err := client.DeleteSession(t.Context(), session.SessionID); err != nil { + t.Fatalf("DeleteSession failed: %v", err) + } + rpcErr := requestSkillProviderError(t, server, "skillProvider.list", map[string]any{"sessionId": session.SessionID}) + assertSkillProviderError(t, rpcErr, "No skill provider for session: delete-session") + if got := provider.snapshotCalls(); len(got) != 0 { + t.Fatalf("provider was called after delete: %v", got) + } + }) + + t.Run("connection loss clears provider", func(t *testing.T) { + client, server, provider, session := newSkillProviderOpenSession(t, "closed-session") + + client.handleConnectionClose() + rpcErr := requestSkillProviderError(t, server, "skillProvider.list", map[string]any{"sessionId": session.SessionID}) + assertSkillProviderError(t, rpcErr, "No skill provider for session: closed-session") + if got := provider.snapshotCalls(); len(got) != 0 { + t.Fatalf("provider was called after connection loss: %v", got) + } + }) + + t.Run("failed create cleanup", func(t *testing.T) { + provider := newTestSkillProvider() + var sessionID string + client, server := newSkillProviderClient(t) + server.SetRequestHandler("session.create", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + sessionID = sessionIDFromParams(t, params) + return nil, &jsonrpc2.Error{Code: -32000, Message: "create failed"} + }) + + if _, err := client.CreateSession(t.Context(), &SessionConfig{SkillProvider: provider}); err == nil { + t.Fatal("CreateSession succeeded unexpectedly") + } + rpcErr := requestSkillProviderError(t, server, "skillProvider.list", map[string]any{"sessionId": sessionID}) + assertSkillProviderError(t, rpcErr, "No skill provider for session: "+sessionID) + if got := provider.snapshotCalls(); len(got) != 0 { + t.Fatalf("provider was called after failed create: %v", got) + } + }) + + t.Run("failed resume cleanup", func(t *testing.T) { + provider := newTestSkillProvider() + client, server := newSkillProviderClient(t) + server.SetRequestHandler("session.resume", func(json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + return nil, &jsonrpc2.Error{Code: -32000, Message: "resume failed"} + }) + + if _, err := client.ResumeSession(t.Context(), "failed-resume", &ResumeSessionConfig{SkillProvider: provider}); err == nil { + t.Fatal("ResumeSession succeeded unexpectedly") + } + rpcErr := requestSkillProviderError(t, server, "skillProvider.list", map[string]any{"sessionId": "failed-resume"}) + assertSkillProviderError(t, rpcErr, "No skill provider for session: failed-resume") + if got := provider.snapshotCalls(); len(got) != 0 { + t.Fatalf("provider was called after failed resume: %v", got) + } + }) +} + +func TestSkillProviderResumeRebinding(t *testing.T) { + t.Run("serves replacement provider", func(t *testing.T) { + client, server := newSkillProviderClient(t) + server.SetRequestHandler("session.create", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + sessionID := sessionIDFromParams(t, params) + return []byte(`{"sessionId":"` + sessionID + `"}`), nil + }) + server.SetRequestHandler("session.resume", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + sessionID := sessionIDFromParams(t, params) + return []byte(`{"sessionId":"` + sessionID + `"}`), nil + }) + original := newTestSkillProvider() + replacement := newTestSkillProvider() + replacement.markdown = map[string]string{"review": "replacement"} + + session, err := client.CreateSession(t.Context(), &SessionConfig{ + SessionID: "rebind-session", + SkillProvider: original, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + if _, err := client.ResumeSession(t.Context(), session.SessionID, &ResumeSessionConfig{SkillProvider: replacement}); err != nil { + t.Fatalf("ResumeSession failed: %v", err) + } + raw, rpcErr := server.Request(t.Context(), "skillProvider.read", map[string]any{ + "sessionId": session.SessionID, + "name": "review", + }) + if rpcErr != nil { + t.Fatalf("skillProvider.read failed: %v", rpcErr) + } + var result rpc.SkillProviderReadResult + if err := json.Unmarshal(raw, &result); err != nil { + t.Fatal(err) + } + if result.Markdown == nil { + t.Fatal("markdown = nil, want replacement") + } + if *result.Markdown != "replacement" { + t.Fatalf("markdown = %q, want replacement", *result.Markdown) + } + if got := original.snapshotCalls(); len(got) != 0 { + t.Fatalf("original provider was called after resume: %v", got) + } + if got := replacement.snapshotCalls(); strings.Join(got, ",") != "read:review" { + t.Fatalf("replacement provider calls = %v", got) + } + }) + + t.Run("resume without provider unbinds previous provider", func(t *testing.T) { + client, server, original, session := newSkillProviderOpenSession(t, "unbind-session") + server.SetRequestHandler("session.resume", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + sessionID := sessionIDFromParams(t, params) + return []byte(`{"sessionId":"` + sessionID + `"}`), nil + }) + + if _, err := client.ResumeSession(t.Context(), session.SessionID, &ResumeSessionConfig{}); err != nil { + t.Fatalf("ResumeSession failed: %v", err) + } + rpcErr := requestSkillProviderError(t, server, "skillProvider.list", map[string]any{"sessionId": session.SessionID}) + assertSkillProviderError(t, rpcErr, "No skill provider for session: unbind-session") + if got := original.snapshotCalls(); len(got) != 0 { + t.Fatalf("original provider was called after unbound resume: %v", got) + } + }) +} + +type panickingSkillProvider struct{} + +func (panickingSkillProvider) ListSkills(context.Context) ([]rpc.SkillProviderDescriptor, error) { + panic("db-password-in-panic") +} + +func (panickingSkillProvider) ReadSkill(context.Context, string) (string, error) { + panic("db-password-in-panic") +} + +// panickyIsError panics when errors.Is asks whether it matches a target. +type panickyIsError struct{} + +func (panickyIsError) Error() string { return "panicky" } + +func (panickyIsError) Is(error) bool { panic("classification panicked") } + +func captureSkillProviderLog(t *testing.T) *ahpTestLog { + t.Helper() + output := new(ahpTestLog) + previous := log.Writer() + log.SetOutput(output) + t.Cleanup(func() { log.SetOutput(previous) }) + return output +} + +func newSkillProviderClient(t *testing.T) (*Client, *jsonrpc2.Client) { + t.Helper() + rpcClient, server, _ := newRuntimeShutdownRpcPair(t) + t.Cleanup(server.Stop) + client := &Client{ + client: rpcClient, + RPC: rpc.NewServerRPC(rpcClient), + sessions: make(map[string]*Session), + } + client.setupNotificationHandler() + return client, server +} + +func newSkillProviderOpenSession(t *testing.T, sessionID string) (*Client, *jsonrpc2.Client, *testSkillProvider, *Session) { + t.Helper() + client, server := newSkillProviderClient(t) + provider := newTestSkillProvider() + server.SetRequestHandler("session.create", func(params json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + gotSessionID := sessionIDFromParams(t, params) + return []byte(`{"sessionId":"` + gotSessionID + `"}`), nil + }) + server.SetRequestHandler("session.detach", func(json.RawMessage) (json.RawMessage, *jsonrpc2.Error) { + return []byte(`{"success":true}`), nil + }) + + session, err := client.CreateSession(t.Context(), &SessionConfig{ + SessionID: sessionID, + SkillProvider: provider, + }) + if err != nil { + t.Fatalf("CreateSession failed: %v", err) + } + return client, server, provider, session +} + +func skillProviderErrorHarness(t *testing.T, provider SkillProvider) (*Client, *jsonrpc2.Client, string) { + t.Helper() + client, server := newSkillProviderClient(t) + session := newSession("error-session", client.client, "", false) + session.registerSkillProvider(provider) + client.sessions[session.SessionID] = session + return client, server, session.SessionID +} + +func requestSkillProviderError(t *testing.T, server *jsonrpc2.Client, method string, params map[string]any) *jsonrpc2.Error { + t.Helper() + _, err := server.Request(t.Context(), method, params) + if err == nil { + t.Fatalf("%s succeeded unexpectedly", method) + } + rpcErr, ok := err.(*jsonrpc2.Error) + if !ok { + t.Fatalf("%s error = %T %v, want *jsonrpc2.Error", method, err, err) + } + return rpcErr +} + +func assertSkillProviderError(t *testing.T, rpcErr *jsonrpc2.Error, wantMessage string) { + t.Helper() + if rpcErr.Code != -32603 || rpcErr.Message != wantMessage { + t.Fatalf("error = %+v, want code -32603 message %q", rpcErr, wantMessage) + } + if rpcErr.Data != nil { + t.Fatalf("error data = %s, want omitted", rpcErr.Data) + } +} + +func assertSkillProviderFlag(t *testing.T, params json.RawMessage, wantPresent 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["hasSkillProvider"] + if !wantPresent { + if present { + t.Fatalf("hasSkillProvider = %v, want omitted", got) + } + return + } + if got != true { + t.Fatalf("hasSkillProvider = %v, want true", got) + } + if _, present := payload["skillProvider"]; present { + t.Fatalf("skillProvider callback was serialized: %v", payload["skillProvider"]) + } +} diff --git a/go/subagent_hooks_test.go b/go/subagent_hooks_test.go new file mode 100644 index 0000000000..8f68cc49d6 --- /dev/null +++ b/go/subagent_hooks_test.go @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package copilot + +import ( + "encoding/json" + "reflect" + "testing" + "time" +) + +func TestSession_SubagentStartHook(t *testing.T) { + session, cleanup := newTestSession() + defer cleanup() + session.SessionID = "parent-session" + + var captured SubagentStartHookInput + session.registerHooks(&SessionHooks{ + OnSubagentStart: func(input SubagentStartHookInput, invocation HookInvocation) (*SubagentStartHookOutput, error) { + captured = input + if invocation.SessionID != "parent-session" { + t.Errorf("invocation session ID = %q, want parent-session", invocation.SessionID) + } + return &SubagentStartHookOutput{AdditionalContext: "read only the requested file"}, nil + }, + }) + + raw := json.RawMessage(`{"sessionId":"parent-session","timestamp":1700000000123,"cwd":"C:\\work","transcriptPath":"C:\\transcript.jsonl","agentName":"explore","agentDisplayName":"Explore Agent","agentDescription":"Find files"}`) + output, err := session.handleHooksInvoke("subagentStart", raw) + if err != nil { + t.Fatalf("subagentStart dispatch failed: %v", err) + } + if captured.SessionID != "parent-session" || !captured.Timestamp.Equal(time.UnixMilli(1700000000123)) || + captured.WorkingDirectory != `C:\work` || captured.TranscriptPath != `C:\transcript.jsonl` || + captured.AgentName != "explore" || captured.AgentDisplayName != "Explore Agent" || + captured.AgentDescription != "Find files" { + t.Errorf("subagentStart input = %+v", captured) + } + assertHookJSON(t, captured, raw) + assertHookJSON(t, output, json.RawMessage(`{"additionalContext":"read only the requested file"}`)) + + session.registerHooks(&SessionHooks{}) + output, err = session.handleHooksInvoke("subagentStart", raw) + if err != nil || output != nil { + t.Errorf("unregistered subagentStart = (%v, %v), want (nil, nil)", output, err) + } +} + +func TestSession_SubagentStopHook(t *testing.T) { + session, cleanup := newTestSession() + defer cleanup() + session.SessionID = "parent-session" + + raw := json.RawMessage(`{"sessionId":"parent-session","timestamp":1700000000456,"cwd":"C:\\work","transcriptPath":"C:\\transcript.jsonl","agentId":"read-file","agentType":"explore","agentName":"explore","agentDisplayName":"Explore Agent","agentDescription":"Find files","stopReason":"end_turn","response":"Hello from subagent test!"}`) + tests := []struct { + name string + output *SubagentStopHookOutput + want json.RawMessage + }{ + { + name: "block with a reason", + output: &SubagentStopHookOutput{Decision: "block", Reason: "Read the file again"}, + want: json.RawMessage(`{"decision":"block","reason":"Read the file again"}`), + }, + { + name: "replace the response", + output: &SubagentStopHookOutput{ModifiedResponse: String("Changed result")}, + want: json.RawMessage(`{"modifiedResponse":"Changed result"}`), + }, + { + name: "replace with an empty response", + output: &SubagentStopHookOutput{ModifiedResponse: String("")}, + want: json.RawMessage(`{"modifiedResponse":""}`), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var captured SubagentStopHookInput + session.registerHooks(&SessionHooks{ + OnSubagentStop: func(input SubagentStopHookInput, invocation HookInvocation) (*SubagentStopHookOutput, error) { + captured = input + if invocation.SessionID != "parent-session" { + t.Errorf("invocation session ID = %q, want parent-session", invocation.SessionID) + } + return tt.output, nil + }, + }) + + output, err := session.handleHooksInvoke("subagentStop", raw) + if err != nil { + t.Fatalf("subagentStop dispatch failed: %v", err) + } + if captured.SessionID != "parent-session" || !captured.Timestamp.Equal(time.UnixMilli(1700000000456)) || + captured.WorkingDirectory != `C:\work` || captured.TranscriptPath != `C:\transcript.jsonl` || + captured.AgentID != "read-file" || captured.AgentType != "explore" || + captured.AgentName != "explore" || captured.AgentDisplayName != "Explore Agent" || + captured.AgentDescription != "Find files" || captured.StopReason != "end_turn" || + captured.Response != "Hello from subagent test!" { + t.Errorf("subagentStop input = %+v", captured) + } + assertHookJSON(t, captured, raw) + assertHookJSON(t, output, tt.want) + }) + } + + session.registerHooks(&SessionHooks{}) + output, err := session.handleHooksInvoke("subagentStop", raw) + if err != nil || output != nil { + t.Errorf("unregistered subagentStop = (%v, %v), want (nil, nil)", output, err) + } +} + +func assertHookJSON(t *testing.T, value any, expected json.RawMessage) { + t.Helper() + gotJSON, err := json.Marshal(value) + if err != nil { + t.Fatalf("marshal %T: %v", value, err) + } + var got, want any + if err := json.Unmarshal(gotJSON, &got); err != nil { + t.Fatalf("decode marshaled %T: %v", value, err) + } + if err := json.Unmarshal(expected, &want); err != nil { + t.Fatalf("decode expected JSON: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("JSON for %T = %s, want %s", value, gotJSON, expected) + } +} diff --git a/go/types.go b/go/types.go index 0ef4fa360d..cdeae3b404 100644 --- a/go/types.go +++ b/go/types.go @@ -3,6 +3,7 @@ package copilot import ( "context" "encoding/json" + "errors" "time" "github.com/github/copilot-sdk/go/rpc" @@ -373,12 +374,14 @@ const ( SectionToolInstructions = "tool_instructions" // SectionCustomInstructions covers repository and organization custom instructions. SectionCustomInstructions = "custom_instructions" - // SectionRuntimeInstructions targets runtime-provided context and instructions - // (e.g. system notifications, memories, workspace context, mode-specific instructions, - // content-exclusion policy). + // SectionRuntimeInstructions targets runtime-provided system-prompt context and + // instructions, such as system notifications, memories, workspace context, and + // content-exclusion policy. Mode-specific instructions can travel in transition + // messages instead. SectionRuntimeInstructions = "runtime_instructions" // SectionLastInstructions covers end-of-prompt instructions: parallel tool calling, - // persistence, and task completion. + // persistence, task completion, and configured subagent-model guidance when the + // task tool is available. SectionLastInstructions = "last_instructions" ) @@ -1027,6 +1030,98 @@ type AgentStopHookOutput struct { // AgentStopHandler handles agent-stop hook invocations. type AgentStopHandler func(input AgentStopHookInput, invocation HookInvocation) (*AgentStopHookOutput, error) +// SubagentStartHookInput is the input before a subagent's first turn. +type SubagentStartHookInput struct { + SessionID string `json:"sessionId"` + Timestamp time.Time `json:"-"` + WorkingDirectory string `json:"cwd"` + TranscriptPath string `json:"transcriptPath"` + AgentName string `json:"agentName"` + AgentDisplayName string `json:"agentDisplayName,omitempty"` + AgentDescription string `json:"agentDescription,omitempty"` +} + +// MarshalJSON implements json.Marshaler, emitting Timestamp as Unix milliseconds. +func (h SubagentStartHookInput) MarshalJSON() ([]byte, error) { + type alias SubagentStartHookInput + return json.Marshal(&struct { + Timestamp int64 `json:"timestamp"` + alias + }{Timestamp: h.Timestamp.UnixMilli(), alias: alias(h)}) +} + +// UnmarshalJSON implements json.Unmarshaler, parsing Timestamp from Unix milliseconds. +func (h *SubagentStartHookInput) UnmarshalJSON(data []byte) error { + type alias SubagentStartHookInput + aux := &struct { + Timestamp int64 `json:"timestamp"` + *alias + }{alias: (*alias)(h)} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + h.Timestamp = time.UnixMilli(aux.Timestamp) + return nil +} + +// SubagentStartHookOutput provides context prepended to the subagent's initial prompt. +type SubagentStartHookOutput struct { + AdditionalContext string `json:"additionalContext,omitempty"` +} + +// SubagentStartHandler handles subagent-start hook invocations. +type SubagentStartHandler func(input SubagentStartHookInput, invocation HookInvocation) (*SubagentStartHookOutput, error) + +// SubagentStopHookInput is the input after a subagent completes a turn. +type SubagentStopHookInput struct { + SessionID string `json:"sessionId"` + Timestamp time.Time `json:"-"` + WorkingDirectory string `json:"cwd"` + TranscriptPath string `json:"transcriptPath"` + AgentID string `json:"agentId,omitempty"` + AgentType string `json:"agentType"` + AgentName string `json:"agentName"` + AgentDisplayName string `json:"agentDisplayName,omitempty"` + AgentDescription string `json:"agentDescription,omitempty"` + StopReason string `json:"stopReason"` + Response string `json:"response"` +} + +// MarshalJSON implements json.Marshaler, emitting Timestamp as Unix milliseconds. +func (h SubagentStopHookInput) MarshalJSON() ([]byte, error) { + type alias SubagentStopHookInput + return json.Marshal(&struct { + Timestamp int64 `json:"timestamp"` + alias + }{Timestamp: h.Timestamp.UnixMilli(), alias: alias(h)}) +} + +// UnmarshalJSON implements json.Unmarshaler, parsing Timestamp from Unix milliseconds. +func (h *SubagentStopHookInput) UnmarshalJSON(data []byte) error { + type alias SubagentStopHookInput + aux := &struct { + Timestamp int64 `json:"timestamp"` + *alias + }{alias: (*alias)(h)} + if err := json.Unmarshal(data, aux); err != nil { + return err + } + h.Timestamp = time.UnixMilli(aux.Timestamp) + return nil +} + +// SubagentStopHookOutput can block a subagent's completion or replace its response. +// Decision must be "block" with a nonempty Reason, "allow", or empty to allow. +// Reason without a block decision and unsupported decisions fail the subagent. +type SubagentStopHookOutput struct { + Decision string `json:"decision,omitempty"` + Reason string `json:"reason,omitempty"` + ModifiedResponse *string `json:"modifiedResponse,omitempty"` +} + +// SubagentStopHandler handles subagent-stop hook invocations. +type SubagentStopHandler func(input SubagentStopHookInput, invocation HookInvocation) (*SubagentStopHookOutput, error) + // PreMCPToolCallHookInput is the input for a pre-mcp-tool-call hook type PreMCPToolCallHookInput struct { SessionID string `json:"sessionId"` @@ -1086,6 +1181,8 @@ type SessionHooks struct { OnSessionEnd SessionEndHandler OnErrorOccurred ErrorOccurredHandler OnAgentStop AgentStopHandler + OnSubagentStart SubagentStartHandler + OnSubagentStop SubagentStopHandler OnPreMCPToolCall PreMCPToolCallHandler } @@ -1226,6 +1323,16 @@ type LargeToolOutputConfig struct { OutputDirectory string `json:"outputDir,omitempty"` } +// ImageGenerationConfig opts in to an authorized Copilot image model. +// Experimental: this API may change or be removed in a future release. +// Omission disables on create/cold resume and preserves resident resume state. +// Not persisted: re-supply after runtime restart. Policy, permissions, offline +// mode, and tool filters still apply. BYOK image generation is not supported. +type ImageGenerationConfig struct { + // Enabled opts in when true and disables image generation when false. + Enabled *bool `json:"enabled,omitempty"` +} + // ToolSearchConfig allows to configure tool search behavior. // Tool search defers tools to keep the model's active tool set small. // To override the tool-search tool's implementation, register a @@ -1242,6 +1349,8 @@ type ToolSearchConfig struct { type SessionFSCapabilities struct { // Sqlite indicates whether the provider supports SQLite query/exists operations. Sqlite bool + // Binary indicates whether the provider supports exact binary file reads and writes. + Binary bool } // SessionFSConfig configures a custom session filesystem provider. @@ -1346,6 +1455,24 @@ const ( AskUserVariantElicitation AskUserVariant = "elicitation" ) +// ErrSkillNotFound is returned by [SkillProvider.ReadSkill] when a listed skill +// is no longer available. It can be wrapped; the SDK checks it with [errors.Is]. +var ErrSkillNotFound = errors.New("skill not found") + +// SkillProvider supplies session-scoped skills from the SDK host. +// +// Experimental: this API may change or be removed in future SDK or CLI +// releases. Implementations must be safe for concurrent calls. +type SkillProvider interface { + // ListSkills returns catalog metadata for skills provided by this session. + // Return nil, nil to publish an empty catalog. + ListSkills(ctx context.Context) ([]rpc.SkillProviderDescriptor, error) + // ReadSkill returns the SKILL.md markdown for name. Return an error wrapping + // [ErrSkillNotFound] when the skill no longer exists. The markdown is + // ignored whenever a non-nil error is returned. + ReadSkill(ctx context.Context, name string) (string, error) +} + // SessionConfig configures a new session type SessionConfig struct { // SessionID is an optional custom session ID @@ -1545,6 +1672,12 @@ type SessionConfig struct { Agent string // SkillDirectories is a list of directories to load skills from SkillDirectories []string + // SkillProvider supplies ephemeral SDK-hosted skills for this session. The + // provider is never serialized or persisted; re-supply it when resuming. + // + // Experimental: this API may change or be removed in future SDK or CLI + // releases. Implementations must be safe for concurrent calls. + SkillProvider SkillProvider `json:"-"` // 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). @@ -1570,6 +1703,9 @@ type SessionConfig struct { // defers rarely used tools behind a searchable index. When nil, the runtime // default applies. ToolSearch *ToolSearchConfig + // ImageGeneration is the non-persisted image generation opt-in. + // Experimental: this API may change or be removed in a future release. + ImageGeneration *ImageGenerationConfig // Memory configures the memory feature for the session. When omitted, the // runtime default applies. Memory *MemoryConfiguration @@ -1680,6 +1816,10 @@ type SessionConfig struct { // be set; if omitted, the runtime is expected to reject session creation // (fail-closed). Unset behaves exactly as before. EnableManagedSettings *bool + // EnforceManagedModelDefaults, when true, enforces locked managed model + // controls for this session. Conflicting changes are rejected; overridable + // managed defaults remain mutable. + EnforceManagedModelDefaults *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 @@ -1723,6 +1863,9 @@ type ManagedSettingsPermissions struct { // session. See the DisableBypassPermissionsMode constants for known values. // Newer values are forwarded unchanged so runtime policies remain fail-closed. DisableBypassPermissionsMode DisableBypassPermissionsMode `json:"disableBypassPermissionsMode,omitempty"` + // DisableAssistedPermissionsMode prevents Assisted Permissions from being + // activated when true. Nil or false imposes no restriction. + DisableAssistedPermissionsMode *bool `json:"disableAssistedPermissionsMode,omitempty"` // Deny lists operations that must always be denied. Unioned across layers. Deny []string `json:"deny,omitzero"` // Ask lists operations that must prompt for approval. Unioned across layers. @@ -1730,6 +1873,11 @@ type ManagedSettingsPermissions struct { // Allow lists operations permitted without prompting. Every declared allow // list across managed layers must admit an operation for it to be allowed. Allow []string `json:"allow,omitzero"` + // LimitTo is a closed-world host boundary expressed as Domain(hostname), + // Domain(IP), or Domain(*.example.com) rules. Schemes, ports, paths, + // queries, and fragments are rejected. Multiple managed layers intersect + // their lists. A present empty list denies all hosts. + LimitTo []string `json:"limitTo,omitzero"` } // ToolDefer controls whether a tool may be deferred (loaded lazily via tool @@ -1789,6 +1937,22 @@ type ToolInvocation struct { // disconnects, so background work must derive its own lifetime if it should // outlive the invocation. TraceContext context.Context + + // AgentID is the sub-agent instance that issued this call (the AgentID of + // its session events). It is empty for a root-agent call, and for every + // call when the runtime does not attribute sub-agent tool calls. + // + // Experimental: this field may change or be removed. + AgentID string + + // RequestID is the runtime request id of this call: the key + // session.tools.handlePendingToolCall and session.tools.listPendingRequests use. + // A root-agent call stays answerable after the session is resumed with + // pending work continued; a sub-agent's call can be answered only while that + // sub-agent is still running. + // + // Experimental: this field may change or be removed. + RequestID string } // ToolHandler executes a tool invocation. @@ -2110,6 +2274,13 @@ type ResumeSessionConfig struct { Agent string // SkillDirectories is a list of directories to load skills from SkillDirectories []string + // SkillProvider supplies ephemeral SDK-hosted skills for this resumed + // session. Re-supply it on every resume; omitting it unbinds any previous + // provider. + // + // Experimental: this API may change or be removed in future SDK or CLI + // releases. Implementations must be safe for concurrent calls. + SkillProvider SkillProvider `json:"-"` // 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). @@ -2134,6 +2305,9 @@ type ResumeSessionConfig struct { // defers rarely used tools behind a searchable index. When nil, the runtime // default applies. ToolSearch *ToolSearchConfig + // ImageGeneration is the non-persisted image generation opt-in. + // Experimental: this API may change or be removed in a future release. + ImageGeneration *ImageGenerationConfig // Memory configures the memory feature for the session. When omitted, the // runtime default applies. Memory *MemoryConfiguration @@ -2225,6 +2399,9 @@ type ResumeSessionConfig struct { // SessionConfig.EnableManagedSettings. Re-supply on resume so the runtime // re-applies the managed-settings self-fetch after a CLI process restart. EnableManagedSettings *bool + // EnforceManagedModelDefaults re-applies managed model lock enforcement on + // resume. See SessionConfig.EnforceManagedModelDefaults. + EnforceManagedModelDefaults *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 @@ -2277,6 +2454,10 @@ type ProviderConfig struct { // Set "websockets" to deliver Responses API requests over a persistent WebSocket // connection instead of HTTP. Applies to OpenAI-compatible providers using WireAPI "responses". Transport string `json:"transport,omitempty"` + // ModelProvider is the product serving the model, reported in telemetry as + // model_provider. Allowed values are "openai", "anthropic", "azure_openai", + // "ollama", "lm_studio", "foundry_local", and "llama_cpp"; only affects telemetry. + ModelProvider string `json:"modelProvider,omitempty"` // BaseURL is the API endpoint URL BaseURL string `json:"baseUrl"` // APIKey is the API key. Optional for local providers like Ollama. @@ -2351,6 +2532,8 @@ const ( AutoTierBalance = rpc.AutoTierBalance // AutoTierIntelligence selects the intelligence routing tier. AutoTierIntelligence = rpc.AutoTierIntelligence + // AutoTierFast selects the integrator-only latency preset. + AutoTierFast = rpc.AutoTierFast ) // CapiSessionOptions configures provider-scoped Copilot API (CAPI) session behavior. @@ -2405,6 +2588,11 @@ type NamedProviderConfig struct { Type string `json:"type,omitempty"` // WireAPI is the API format (openai/azure only): "completions" or "responses". Defaults to "completions". WireAPI string `json:"wireApi,omitempty"` + // ModelProvider is the product serving this provider's models, reported in + // telemetry as model_provider. Allowed values are "openai", "anthropic", + // "azure_openai", "ollama", "lm_studio", "foundry_local", and "llama_cpp"; + // only affects telemetry. + ModelProvider string `json:"modelProvider,omitempty"` // BaseURL is the API endpoint URL. BaseURL string `json:"baseUrl"` // APIKey is the API key. Optional for local providers like Ollama. @@ -2719,6 +2907,7 @@ type createSessionRequest struct { EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` EnableSessionStore *bool `json:"enableSessionStore,omitempty"` EnableSkills *bool `json:"enableSkills,omitempty"` + HasSkillProvider *bool `json:"hasSkillProvider,omitempty"` SkillDirectories []string `json:"skillDirectories,omitempty"` PluginDirectories []string `json:"pluginDirectories,omitempty"` InstructionDirectories []string `json:"instructionDirectories,omitempty"` @@ -2727,6 +2916,7 @@ type createSessionRequest struct { InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` LargeOutput *LargeToolOutputConfig `json:"largeOutput,omitempty"` ToolSearch *ToolSearchConfig `json:"toolSearch,omitempty"` + ImageGeneration *ImageGenerationConfig `json:"imageGeneration,omitempty"` Memory *MemoryConfiguration `json:"memory,omitempty"` Commands []wireCommand `json:"commands,omitempty"` RequestElicitation *bool `json:"requestElicitation,omitempty"` @@ -2745,6 +2935,7 @@ type createSessionRequest struct { ExpAssignments *CopilotExpAssignmentResponse `json:"expAssignments,omitempty"` FeatureFlags *map[string]bool `json:"featureFlags,omitempty"` EnableManagedSettings *bool `json:"enableManagedSettings,omitempty"` + EnforceManagedModelDefaults *bool `json:"enforceManagedModelDefaults,omitempty"` ManagedSettings *ManagedSettings `json:"managedSettings,omitempty"` Traceparent string `json:"traceparent,omitempty"` Tracestate string `json:"tracestate,omitempty"` @@ -2809,6 +3000,7 @@ type resumeSessionRequest struct { EnableHostGitOperations *bool `json:"enableHostGitOperations,omitempty"` EnableSessionStore *bool `json:"enableSessionStore,omitempty"` EnableSkills *bool `json:"enableSkills,omitempty"` + HasSkillProvider *bool `json:"hasSkillProvider,omitempty"` DisableResume *bool `json:"disableResume,omitempty"` ContinuePendingWork *bool `json:"continuePendingWork,omitempty"` AllowTranscriptRecovery *bool `json:"allowTranscriptRecovery,omitempty"` @@ -2831,6 +3023,7 @@ type resumeSessionRequest struct { InfiniteSessions *InfiniteSessionConfig `json:"infiniteSessions,omitempty"` LargeOutput *LargeToolOutputConfig `json:"largeOutput,omitempty"` ToolSearch *ToolSearchConfig `json:"toolSearch,omitempty"` + ImageGeneration *ImageGenerationConfig `json:"imageGeneration,omitempty"` Memory *MemoryConfiguration `json:"memory,omitempty"` Commands []wireCommand `json:"commands,omitempty"` RequestElicitation *bool `json:"requestElicitation,omitempty"` @@ -2849,6 +3042,7 @@ type resumeSessionRequest struct { ExpAssignments *CopilotExpAssignmentResponse `json:"expAssignments,omitempty"` FeatureFlags *map[string]bool `json:"featureFlags,omitempty"` EnableManagedSettings *bool `json:"enableManagedSettings,omitempty"` + EnforceManagedModelDefaults *bool `json:"enforceManagedModelDefaults,omitempty"` ManagedSettings *ManagedSettings `json:"managedSettings,omitempty"` Traceparent string `json:"traceparent,omitempty"` Tracestate string `json:"tracestate,omitempty"` diff --git a/go/types_test.go b/go/types_test.go index ad7972fe15..7c9afb6274 100644 --- a/go/types_test.go +++ b/go/types_test.go @@ -124,6 +124,7 @@ func TestProviderConfig_JSONIncludesAllFields(t *testing.T) { Headers: map[string]string{"Authorization": "Bearer provider-token"}, ModelID: "gpt-4o", WireModel: "my-finetune-v3", + ModelProvider: "lm_studio", MaxPromptTokens: 100000, MaxOutputTokens: 4096, } @@ -147,6 +148,9 @@ func TestProviderConfig_JSONIncludesAllFields(t *testing.T) { if decoded["wireModel"] != "my-finetune-v3" { t.Errorf("expected wireModel 'my-finetune-v3', got %v", decoded["wireModel"]) } + if decoded["modelProvider"] != "lm_studio" { + t.Errorf("expected modelProvider 'lm_studio', got %v", decoded["modelProvider"]) + } if decoded["maxPromptTokens"] != float64(100000) { t.Errorf("expected maxPromptTokens 100000, got %v", decoded["maxPromptTokens"]) } @@ -175,13 +179,36 @@ func TestProviderConfig_JSONOmitsUnsetTokenFields(t *testing.T) { t.Fatalf("failed to unmarshal ProviderConfig: %v", err) } - for _, field := range []string{"modelId", "wireModel", "maxPromptTokens", "maxOutputTokens", "headers"} { + for _, field := range []string{"modelId", "wireModel", "modelProvider", "maxPromptTokens", "maxOutputTokens", "headers"} { if _, present := decoded[field]; present { t.Errorf("expected %q to be omitted when unset, got %v", field, decoded[field]) } } } +func TestNamedProviderConfig_JSONIncludesModelProvider(t *testing.T) { + cfg := NamedProviderConfig{ + Name: "local", + Type: "openai", + BaseURL: "http://localhost:11434/v1", + ModelProvider: "ollama", + } + + data, err := json.Marshal(cfg) + if err != nil { + t.Fatalf("failed to marshal NamedProviderConfig: %v", err) + } + + var decoded map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("failed to unmarshal NamedProviderConfig: %v", err) + } + + if decoded["modelProvider"] != "ollama" { + t.Errorf("expected modelProvider 'ollama', got %v", decoded["modelProvider"]) + } +} + func TestCustomAgentConfig_JSONIncludesModel(t *testing.T) { cfg := CustomAgentConfig{ Name: "model-agent", diff --git a/go/zsession_events.go b/go/zsession_events.go index d91f36537d..0c4775d386 100644 --- a/go/zsession_events.go +++ b/go/zsession_events.go @@ -13,6 +13,7 @@ type ( AgentInterruptedCancelPhase = rpc.AgentInterruptedCancelPhase AgentInterruptedData = rpc.AgentInterruptedData AgentModelPolicy = rpc.AgentModelPolicy + AiCreditsStatus = rpc.AiCreditsStatus AssistantFusionPhaseActivityData = rpc.AssistantFusionPhaseActivityData AssistantFusionPhaseCompletedData = rpc.AssistantFusionPhaseCompletedData AssistantFusionPhaseFailedData = rpc.AssistantFusionPhaseFailedData @@ -143,7 +144,15 @@ type ( HookEndError = rpc.HookEndError HookProgressData = rpc.HookProgressData HookStartData = rpc.HookStartData + HumanResponseActor = rpc.HumanResponseActor + HumanResponseRecordedData = rpc.HumanResponseRecordedData + HumanResponseRecordedResponse = rpc.HumanResponseRecordedResponse + HumanResponseRecordedResponseAskUser = rpc.HumanResponseRecordedResponseAskUser + HumanResponseRecordedResponseExitPlanMode = rpc.HumanResponseRecordedResponseExitPlanMode + HumanResponseRecordedResponseResponseKind = rpc.HumanResponseRecordedResponseResponseKind + HumanResponseRecordedResponseUserInput = rpc.HumanResponseRecordedResponseUserInput IndexedSearchState = rpc.IndexedSearchState + ManagedPluginProgressPhase = rpc.ManagedPluginProgressPhase ManagedSettingsEnforcedAction = rpc.ManagedSettingsEnforcedAction ManagedSettingsEnforcedEscalation = rpc.ManagedSettingsEnforcedEscalation ManagedSettingsResolvedData = rpc.ManagedSettingsResolvedData @@ -183,8 +192,11 @@ type ( ModelCallFinishedData = rpc.ModelCallFinishedData ModelCallFinishedOutcome = rpc.ModelCallFinishedOutcome ModelCallStartData = rpc.ModelCallStartData + ModelCallWebSocketFallbackReason = rpc.ModelCallWebSocketFallbackReason ModelChangeSource = rpc.ModelChangeSource ModelDeselectedReason = rpc.ModelDeselectedReason + ModelProviderKind = rpc.ModelProviderKind + ModelProviderRef = rpc.ModelProviderRef OmittedBinaryOmittedReason = rpc.OmittedBinaryOmittedReason OmittedBinaryResult = rpc.OmittedBinaryResult OmittedBinaryType = rpc.OmittedBinaryType @@ -265,6 +277,8 @@ type ( PermissionRule = rpc.PermissionRule PermissionSandboxPathGrant = rpc.PermissionSandboxPathGrant PermissionSandboxPathGrantAccess = rpc.PermissionSandboxPathGrantAccess + PermissionWriteFileContent = rpc.PermissionWriteFileContent + PermissionWriteFileEdit = rpc.PermissionWriteFileEdit PersistedBinaryImage = rpc.PersistedBinaryImage PersistedBinaryImageType = rpc.PersistedBinaryImageType PersistedBinaryResult = rpc.PersistedBinaryResult @@ -272,7 +286,16 @@ type ( PlanChangedOperation = rpc.PlanChangedOperation PossibleURL = rpc.PossibleURL PromptCacheBreakData = rpc.PromptCacheBreakData + ProviderQuotaAccessState = rpc.ProviderQuotaAccessState + ProviderQuotaAcquisitionStatus = rpc.ProviderQuotaAcquisitionStatus + ProviderQuotaBudgetMetadata = rpc.ProviderQuotaBudgetMetadata + ProviderQuotaCapacityState = rpc.ProviderQuotaCapacityState + ProviderQuotaObservationKind = rpc.ProviderQuotaObservationKind + ProviderQuotaQuantityKind = rpc.ProviderQuotaQuantityKind + ProviderQuotaState = rpc.ProviderQuotaState + ProviderQuotaUnit = rpc.ProviderQuotaUnit RawCitationLocation = rpc.RawCitationLocation + RawHumanResponseRecordedResponse = rpc.RawHumanResponseRecordedResponse RawPermissionPromptRequest = rpc.RawPermissionPromptRequest RawPermissionRequest = rpc.RawPermissionRequest RawPermissionResult = rpc.RawPermissionResult @@ -327,6 +350,7 @@ type ( SessionLimitsExhaustedRequestedData = rpc.SessionLimitsExhaustedRequestedData SessionLimitsExhaustedResponse = rpc.SessionLimitsExhaustedResponse SessionLimitsExhaustedResponseAction = rpc.SessionLimitsExhaustedResponseAction + SessionManagedPluginProgressData = rpc.SessionManagedPluginProgressData SessionManagedSettingsEnforcedData = rpc.SessionManagedSettingsEnforcedData SessionManagedSettingsResolvedData = rpc.SessionManagedSettingsResolvedData SessionMCPServerNeedsReconnectData = rpc.SessionMCPServerNeedsReconnectData @@ -341,6 +365,7 @@ type ( SessionPermissionRecoveryData = rpc.SessionPermissionRecoveryData SessionPermissionsChangedData = rpc.SessionPermissionsChangedData SessionPlanChangedData = rpc.SessionPlanChangedData + SessionQuotaObservationData = rpc.SessionQuotaObservationData SessionRemoteSteerableChangedData = rpc.SessionRemoteSteerableChangedData SessionResumeData = rpc.SessionResumeData SessionScheduleCancelledData = rpc.SessionScheduleCancelledData @@ -422,6 +447,8 @@ type ( ToolExecutionCompleteContentType = rpc.ToolExecutionCompleteContentType ToolExecutionCompleteData = rpc.ToolExecutionCompleteData ToolExecutionCompleteError = rpc.ToolExecutionCompleteError + ToolExecutionCompleteFileEdit = rpc.ToolExecutionCompleteFileEdit + ToolExecutionCompleteFileEditKind = rpc.ToolExecutionCompleteFileEditKind ToolExecutionCompleteResult = rpc.ToolExecutionCompleteResult ToolExecutionCompleteShellExecution = rpc.ToolExecutionCompleteShellExecution ToolExecutionCompleteToolDescription = rpc.ToolExecutionCompleteToolDescription @@ -437,45 +464,58 @@ type ( ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite = rpc.ToolExecutionCompleteUIResourceMetaUIPermissionsClipboardWrite ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation = rpc.ToolExecutionCompleteUIResourceMetaUIPermissionsGeolocation ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone = rpc.ToolExecutionCompleteUIResourceMetaUIPermissionsMicrophone - ToolExecutionPartialResultData = rpc.ToolExecutionPartialResultData - ToolExecutionProgressData = rpc.ToolExecutionProgressData - ToolExecutionStartData = rpc.ToolExecutionStartData - ToolExecutionStartShellToolInfo = rpc.ToolExecutionStartShellToolInfo - ToolExecutionStartToolDescription = rpc.ToolExecutionStartToolDescription - ToolExecutionStartToolDescriptionMeta = rpc.ToolExecutionStartToolDescriptionMeta - ToolExecutionStartToolDescriptionMetaUI = rpc.ToolExecutionStartToolDescriptionMetaUI - ToolExecutionStartToolDescriptionMetaUIVisibility = rpc.ToolExecutionStartToolDescriptionMetaUIVisibility - ToolSearchActivatedData = rpc.ToolSearchActivatedData - ToolUserRequestedData = rpc.ToolUserRequestedData - UIEphemeralQueryData = rpc.UIEphemeralQueryData - UIEphemeralQueryPhase = rpc.UIEphemeralQueryPhase - UserInputCompletedData = rpc.UserInputCompletedData - UserInputRequestedData = rpc.UserInputRequestedData - UserMessageAgentMode = rpc.UserMessageAgentMode - UserMessageData = rpc.UserMessageData - UserMessageDelivery = rpc.UserMessageDelivery - UserToolSessionApproval = rpc.UserToolSessionApproval - UserToolSessionApprovalCommands = rpc.UserToolSessionApprovalCommands - UserToolSessionApprovalCustomTool = rpc.UserToolSessionApprovalCustomTool - UserToolSessionApprovalExtensionEnvAccess = rpc.UserToolSessionApprovalExtensionEnvAccess - UserToolSessionApprovalExtensionManagement = rpc.UserToolSessionApprovalExtensionManagement - UserToolSessionApprovalExtensionPermissionAccess = rpc.UserToolSessionApprovalExtensionPermissionAccess - UserToolSessionApprovalKind = rpc.UserToolSessionApprovalKind - UserToolSessionApprovalMCP = rpc.UserToolSessionApprovalMCP - UserToolSessionApprovalMemory = rpc.UserToolSessionApprovalMemory - UserToolSessionApprovalRead = rpc.UserToolSessionApprovalRead - UserToolSessionApprovalWorkflow = rpc.UserToolSessionApprovalWorkflow - UserToolSessionApprovalWrite = rpc.UserToolSessionApprovalWrite - Verbosity = rpc.Verbosity - WorkflowPermissionOperation = rpc.WorkflowPermissionOperation - WorkflowPermissionPhase = rpc.WorkflowPermissionPhase - WorkflowRunSettledData = rpc.WorkflowRunSettledData - WorkflowRunSettledStatus = rpc.WorkflowRunSettledStatus - WorkflowRunStartedData = rpc.WorkflowRunStartedData - WorkflowRunUpdatedData = rpc.WorkflowRunUpdatedData - WorkingDirectoryContext = rpc.WorkingDirectoryContext - WorkingDirectoryContextHostType = rpc.WorkingDirectoryContextHostType - WorkspaceFileChangedOperation = rpc.WorkspaceFileChangedOperation + // Deprecated: ToolExecutionPartialResultData is deprecated. + ToolExecutionPartialResultData = rpc.ToolExecutionPartialResultData + ToolExecutionProgressData = rpc.ToolExecutionProgressData + ToolExecutionStartData = rpc.ToolExecutionStartData + ToolExecutionStartShellToolInfo = rpc.ToolExecutionStartShellToolInfo + ToolExecutionStartToolDescription = rpc.ToolExecutionStartToolDescription + ToolExecutionStartToolDescriptionMeta = rpc.ToolExecutionStartToolDescriptionMeta + ToolExecutionStartToolDescriptionMetaUI = rpc.ToolExecutionStartToolDescriptionMetaUI + ToolExecutionStartToolDescriptionMetaUIVisibility = rpc.ToolExecutionStartToolDescriptionMetaUIVisibility + ToolSearchActivatedData = rpc.ToolSearchActivatedData + ToolShellOutputData = rpc.ToolShellOutputData + ToolShellOutputStream = rpc.ToolShellOutputStream + ToolUserRequestedData = rpc.ToolUserRequestedData + UIEphemeralQueryData = rpc.UIEphemeralQueryData + UIEphemeralQueryPhase = rpc.UIEphemeralQueryPhase + UsageAccountingIdentity = rpc.UsageAccountingIdentity + UsageGetMetricsResult = rpc.UsageGetMetricsResult + UsageMetricsAgentMetric = rpc.UsageMetricsAgentMetric + UsageMetricsCodeChanges = rpc.UsageMetricsCodeChanges + UsageMetricsModelMetric = rpc.UsageMetricsModelMetric + UsageMetricsModelMetricRequests = rpc.UsageMetricsModelMetricRequests + UsageMetricsModelMetricTokenDetail = rpc.UsageMetricsModelMetricTokenDetail + UsageMetricsModelMetricUsage = rpc.UsageMetricsModelMetricUsage + UsageMetricsProviderModelMetric = rpc.UsageMetricsProviderModelMetric + UsageMetricsTokenDetail = rpc.UsageMetricsTokenDetail + UserInputCompletedData = rpc.UserInputCompletedData + UserInputRequestedData = rpc.UserInputRequestedData + UserMessageAgentMode = rpc.UserMessageAgentMode + UserMessageData = rpc.UserMessageData + UserMessageDelivery = rpc.UserMessageDelivery + UserToolSessionApproval = rpc.UserToolSessionApproval + UserToolSessionApprovalCommands = rpc.UserToolSessionApprovalCommands + UserToolSessionApprovalCustomTool = rpc.UserToolSessionApprovalCustomTool + UserToolSessionApprovalExtensionEnvAccess = rpc.UserToolSessionApprovalExtensionEnvAccess + UserToolSessionApprovalExtensionManagement = rpc.UserToolSessionApprovalExtensionManagement + UserToolSessionApprovalExtensionPermissionAccess = rpc.UserToolSessionApprovalExtensionPermissionAccess + UserToolSessionApprovalKind = rpc.UserToolSessionApprovalKind + UserToolSessionApprovalMCP = rpc.UserToolSessionApprovalMCP + UserToolSessionApprovalMemory = rpc.UserToolSessionApprovalMemory + UserToolSessionApprovalRead = rpc.UserToolSessionApprovalRead + UserToolSessionApprovalWorkflow = rpc.UserToolSessionApprovalWorkflow + UserToolSessionApprovalWrite = rpc.UserToolSessionApprovalWrite + Verbosity = rpc.Verbosity + WorkflowPermissionOperation = rpc.WorkflowPermissionOperation + WorkflowPermissionPhase = rpc.WorkflowPermissionPhase + WorkflowRunSettledData = rpc.WorkflowRunSettledData + WorkflowRunSettledStatus = rpc.WorkflowRunSettledStatus + WorkflowRunStartedData = rpc.WorkflowRunStartedData + WorkflowRunUpdatedData = rpc.WorkflowRunUpdatedData + WorkingDirectoryContext = rpc.WorkingDirectoryContext + WorkingDirectoryContextHostType = rpc.WorkingDirectoryContextHostType + WorkspaceFileChangedOperation = rpc.WorkspaceFileChangedOperation ) // Session-event constants are generated in the rpc package and re-exported here for source compatibility. @@ -492,6 +532,9 @@ const ( AgentInterruptedCancelPhasePreFirstToken = rpc.AgentInterruptedCancelPhasePreFirstToken AgentModelPolicyPreferred = rpc.AgentModelPolicyPreferred AgentModelPolicyRequired = rpc.AgentModelPolicyRequired + AiCreditsStatusComplete = rpc.AiCreditsStatusComplete + AiCreditsStatusPartial = rpc.AiCreditsStatusPartial + AiCreditsStatusUnavailable = rpc.AiCreditsStatusUnavailable AssistantMessageToolRequestCallerTypeProgram = rpc.AssistantMessageToolRequestCallerTypeProgram AssistantMessageToolRequestTypeCustom = rpc.AssistantMessageToolRequestTypeCustom AssistantMessageToolRequestTypeFunction = rpc.AssistantMessageToolRequestTypeFunction @@ -541,7 +584,6 @@ const ( AutopilotObjectiveChangedStatusCapReached = rpc.AutopilotObjectiveChangedStatusCapReached AutopilotObjectiveChangedStatusCompleted = rpc.AutopilotObjectiveChangedStatusCompleted AutopilotObjectiveChangedStatusPaused = rpc.AutopilotObjectiveChangedStatusPaused - AutoTierFast = rpc.AutoTierFast AutoTierSwitchFailureReasonPolicyRejected = rpc.AutoTierSwitchFailureReasonPolicyRejected AutoTierSwitchFailureReasonRequestFailed = rpc.AutoTierSwitchFailureReasonRequestFailed AutoTierSwitchFailureReasonSetupFailed = rpc.AutoTierSwitchFailureReasonSetupFailed @@ -616,11 +658,21 @@ const ( FusionTurnKindUser = rpc.FusionTurnKindUser HandoffSourceTypeLocal = rpc.HandoffSourceTypeLocal HandoffSourceTypeRemote = rpc.HandoffSourceTypeRemote + HumanResponseActorHostAutomation = rpc.HumanResponseActorHostAutomation + HumanResponseActorHumanResponse = rpc.HumanResponseActorHumanResponse + HumanResponseActorUnknown = rpc.HumanResponseActorUnknown + HumanResponseRecordedResponseResponseKindAskUser = rpc.HumanResponseRecordedResponseResponseKindAskUser + HumanResponseRecordedResponseResponseKindExitPlanMode = rpc.HumanResponseRecordedResponseResponseKindExitPlanMode + HumanResponseRecordedResponseResponseKindUserInput = rpc.HumanResponseRecordedResponseResponseKindUserInput IndexedSearchStateDisabled = rpc.IndexedSearchStateDisabled IndexedSearchStateEnabled = rpc.IndexedSearchStateEnabled IndexedSearchStateFailed = rpc.IndexedSearchStateFailed IndexedSearchStateReady = rpc.IndexedSearchStateReady IndexedSearchStateStarting = rpc.IndexedSearchStateStarting + ManagedPluginProgressPhaseComplete = rpc.ManagedPluginProgressPhaseComplete + ManagedPluginProgressPhaseInitializing = rpc.ManagedPluginProgressPhaseInitializing + ManagedPluginProgressPhaseInstalling = rpc.ManagedPluginProgressPhaseInstalling + ManagedPluginProgressPhaseUpdating = rpc.ManagedPluginProgressPhaseUpdating ManagedSettingsEnforcedActionBypassPermissionsBlocked = rpc.ManagedSettingsEnforcedActionBypassPermissionsBlocked ManagedSettingsEnforcedEscalationAllowAll = rpc.ManagedSettingsEnforcedEscalationAllowAll ManagedSettingsEnforcedEscalationApproveAll = rpc.ManagedSettingsEnforcedEscalationApproveAll @@ -648,6 +700,7 @@ const ( MCPOauthRequestReasonRefresh = rpc.MCPOauthRequestReasonRefresh MCPOauthRequestReasonUpscope = rpc.MCPOauthRequestReasonUpscope MCPOauthRequiredStaticClientConfigGrantTypeClientCredentials = rpc.MCPOauthRequiredStaticClientConfigGrantTypeClientCredentials + MCPServerSourceAccount = rpc.MCPServerSourceAccount MCPServerSourceBuiltin = rpc.MCPServerSourceBuiltin MCPServerSourceManaged = rpc.MCPServerSourceManaged MCPServerSourcePlugin = rpc.MCPServerSourcePlugin @@ -685,6 +738,11 @@ const ( ModelCallFinishedOutcomeError = rpc.ModelCallFinishedOutcomeError ModelCallFinishedOutcomeRejected = rpc.ModelCallFinishedOutcomeRejected ModelCallFinishedOutcomeSuccess = rpc.ModelCallFinishedOutcomeSuccess + ModelCallWebSocketFallbackReasonAPIError = rpc.ModelCallWebSocketFallbackReasonAPIError + ModelCallWebSocketFallbackReasonConnectFailed = rpc.ModelCallWebSocketFallbackReasonConnectFailed + ModelCallWebSocketFallbackReasonConnectionUnavailable = rpc.ModelCallWebSocketFallbackReasonConnectionUnavailable + ModelCallWebSocketFallbackReasonSendFailed = rpc.ModelCallWebSocketFallbackReasonSendFailed + ModelCallWebSocketFallbackReasonTransportFailed = rpc.ModelCallWebSocketFallbackReasonTransportFailed ModelChangeSourceAgent = rpc.ModelChangeSourceAgent ModelChangeSourceAutomatic = rpc.ModelChangeSourceAutomatic ModelChangeSourceAutoTierRecommendation = rpc.ModelChangeSourceAutoTierRecommendation @@ -699,6 +757,8 @@ const ( ModelChangeSourceSettingsCommand = rpc.ModelChangeSourceSettingsCommand ModelChangeSourceStartup = rpc.ModelChangeSourceStartup ModelDeselectedReasonProviderWithdrawn = rpc.ModelDeselectedReasonProviderWithdrawn + ModelProviderKindCopilot = rpc.ModelProviderKindCopilot + ModelProviderKindLoki = rpc.ModelProviderKindLoki OmittedBinaryOmittedReasonAssetUnavailable = rpc.OmittedBinaryOmittedReasonAssetUnavailable OmittedBinaryOmittedReasonTooLarge = rpc.OmittedBinaryOmittedReasonTooLarge OmittedBinaryTypeImage = rpc.OmittedBinaryTypeImage @@ -717,6 +777,10 @@ const ( PermissionApprovalEvaluationReasonCodeActionTooLong = rpc.PermissionApprovalEvaluationReasonCodeActionTooLong PermissionApprovalEvaluationReasonCodeArgumentBindingUnreviewable = rpc.PermissionApprovalEvaluationReasonCodeArgumentBindingUnreviewable PermissionApprovalEvaluationReasonCodeAuthorizationHistoryIncomplete = rpc.PermissionApprovalEvaluationReasonCodeAuthorizationHistoryIncomplete + PermissionApprovalEvaluationReasonCodeContentExcluded = rpc.PermissionApprovalEvaluationReasonCodeContentExcluded + PermissionApprovalEvaluationReasonCodeDynamicSource = rpc.PermissionApprovalEvaluationReasonCodeDynamicSource + PermissionApprovalEvaluationReasonCodeExecutableTooLarge = rpc.PermissionApprovalEvaluationReasonCodeExecutableTooLarge + PermissionApprovalEvaluationReasonCodeExecutableUnavailable = rpc.PermissionApprovalEvaluationReasonCodeExecutableUnavailable PermissionApprovalEvaluationReasonCodeInactive = rpc.PermissionApprovalEvaluationReasonCodeInactive PermissionApprovalEvaluationReasonCodeInherited = rpc.PermissionApprovalEvaluationReasonCodeInherited PermissionApprovalEvaluationReasonCodeInterpreterTooLarge = rpc.PermissionApprovalEvaluationReasonCodeInterpreterTooLarge @@ -735,11 +799,14 @@ const ( PermissionApprovalEvaluationReasonCodeSandboxBypass = rpc.PermissionApprovalEvaluationReasonCodeSandboxBypass PermissionApprovalEvaluationReasonCodeShellEnvironmentUnreviewable = rpc.PermissionApprovalEvaluationReasonCodeShellEnvironmentUnreviewable PermissionApprovalEvaluationReasonCodeTooLarge = rpc.PermissionApprovalEvaluationReasonCodeTooLarge + PermissionApprovalEvaluationReasonCodeTooManySources = rpc.PermissionApprovalEvaluationReasonCodeTooManySources PermissionApprovalEvaluationReasonCodeUnavailable = rpc.PermissionApprovalEvaluationReasonCodeUnavailable PermissionApprovalEvaluationReasonCodeUnknown = rpc.PermissionApprovalEvaluationReasonCodeUnknown PermissionApprovalEvaluationReasonCodeUnreadable = rpc.PermissionApprovalEvaluationReasonCodeUnreadable PermissionApprovalEvaluationReasonCodeUnrepresentablePath = rpc.PermissionApprovalEvaluationReasonCodeUnrepresentablePath PermissionApprovalEvaluationReasonCodeUnreviewableScriptInvocation = rpc.PermissionApprovalEvaluationReasonCodeUnreviewableScriptInvocation + PermissionApprovalEvaluationReasonCodeUnsupportedCommandShape = rpc.PermissionApprovalEvaluationReasonCodeUnsupportedCommandShape + PermissionApprovalEvaluationReasonCodeUnsupportedSource = rpc.PermissionApprovalEvaluationReasonCodeUnsupportedSource PermissionDecisionSourceAuthorizationCarryForward = rpc.PermissionDecisionSourceAuthorizationCarryForward PermissionMessageAuthorizationPolarityDenial = rpc.PermissionMessageAuthorizationPolarityDenial PermissionMessageAuthorizationPolarityGrant = rpc.PermissionMessageAuthorizationPolarityGrant @@ -829,6 +896,30 @@ const ( PlanChangedOperationCreate = rpc.PlanChangedOperationCreate PlanChangedOperationDelete = rpc.PlanChangedOperationDelete PlanChangedOperationUpdate = rpc.PlanChangedOperationUpdate + ProviderQuotaAccessStateAllowed = rpc.ProviderQuotaAccessStateAllowed + ProviderQuotaAccessStateDenied = rpc.ProviderQuotaAccessStateDenied + ProviderQuotaAccessStateNotRequired = rpc.ProviderQuotaAccessStateNotRequired + ProviderQuotaAccessStateUnavailable = rpc.ProviderQuotaAccessStateUnavailable + ProviderQuotaAccessStateUnknown = rpc.ProviderQuotaAccessStateUnknown + ProviderQuotaAcquisitionStatusFailed = rpc.ProviderQuotaAcquisitionStatusFailed + ProviderQuotaAcquisitionStatusSucceeded = rpc.ProviderQuotaAcquisitionStatusSucceeded + ProviderQuotaAcquisitionStatusUnavailable = rpc.ProviderQuotaAcquisitionStatusUnavailable + ProviderQuotaCapacityStateAvailable = rpc.ProviderQuotaCapacityStateAvailable + ProviderQuotaCapacityStateExhausted = rpc.ProviderQuotaCapacityStateExhausted + ProviderQuotaCapacityStateNotApplicable = rpc.ProviderQuotaCapacityStateNotApplicable + ProviderQuotaCapacityStateNotRequired = rpc.ProviderQuotaCapacityStateNotRequired + ProviderQuotaCapacityStateUnavailable = rpc.ProviderQuotaCapacityStateUnavailable + ProviderQuotaCapacityStateUnknown = rpc.ProviderQuotaCapacityStateUnknown + ProviderQuotaCapacityStateUnlimited = rpc.ProviderQuotaCapacityStateUnlimited + ProviderQuotaObservationKindAccountSnapshot = rpc.ProviderQuotaObservationKindAccountSnapshot + ProviderQuotaObservationKindAdmissionState = rpc.ProviderQuotaObservationKindAdmissionState + ProviderQuotaQuantityKindAdvisoryBalance = rpc.ProviderQuotaQuantityKindAdvisoryBalance + ProviderQuotaQuantityKindAuthoritativeBudget = rpc.ProviderQuotaQuantityKindAuthoritativeBudget + ProviderQuotaQuantityKindNone = rpc.ProviderQuotaQuantityKindNone + ProviderQuotaUnitAiCredits = rpc.ProviderQuotaUnitAiCredits + ProviderQuotaUnitRequests = rpc.ProviderQuotaUnitRequests + ProviderQuotaUnitTokens = rpc.ProviderQuotaUnitTokens + ProviderQuotaUnitUnknown = rpc.ProviderQuotaUnitUnknown ReasoningSummaryConcise = rpc.ReasoningSummaryConcise ReasoningSummaryDetailed = rpc.ReasoningSummaryDetailed ReasoningSummaryNone = rpc.ReasoningSummaryNone @@ -878,6 +969,7 @@ const ( SessionEventTypeHookEnd = rpc.SessionEventTypeHookEnd SessionEventTypeHookProgress = rpc.SessionEventTypeHookProgress SessionEventTypeHookStart = rpc.SessionEventTypeHookStart + SessionEventTypeHumanResponseRecorded = rpc.SessionEventTypeHumanResponseRecorded SessionEventTypeMCPAppToolCallComplete = rpc.SessionEventTypeMCPAppToolCallComplete SessionEventTypeMCPHeadersRefreshCompleted = rpc.SessionEventTypeMCPHeadersRefreshCompleted SessionEventTypeMCPHeadersRefreshRequired = rpc.SessionEventTypeMCPHeadersRefreshRequired @@ -935,6 +1027,7 @@ const ( SessionEventTypeSessionInfo = rpc.SessionEventTypeSessionInfo SessionEventTypeSessionLimitsExhaustedCompleted = rpc.SessionEventTypeSessionLimitsExhaustedCompleted SessionEventTypeSessionLimitsExhaustedRequested = rpc.SessionEventTypeSessionLimitsExhaustedRequested + SessionEventTypeSessionManagedPluginProgress = rpc.SessionEventTypeSessionManagedPluginProgress SessionEventTypeSessionManagedSettingsEnforced = rpc.SessionEventTypeSessionManagedSettingsEnforced SessionEventTypeSessionManagedSettingsResolved = rpc.SessionEventTypeSessionManagedSettingsResolved SessionEventTypeSessionMCPServerNeedsReconnect = rpc.SessionEventTypeSessionMCPServerNeedsReconnect @@ -948,6 +1041,7 @@ const ( SessionEventTypeSessionPermissionRecovery = rpc.SessionEventTypeSessionPermissionRecovery SessionEventTypeSessionPermissionsChanged = rpc.SessionEventTypeSessionPermissionsChanged SessionEventTypeSessionPlanChanged = rpc.SessionEventTypeSessionPlanChanged + SessionEventTypeSessionQuotaObservation = rpc.SessionEventTypeSessionQuotaObservation SessionEventTypeSessionRemoteSteerableChanged = rpc.SessionEventTypeSessionRemoteSteerableChanged SessionEventTypeSessionResume = rpc.SessionEventTypeSessionResume SessionEventTypeSessionScheduleCancelled = rpc.SessionEventTypeSessionScheduleCancelled @@ -980,119 +1074,127 @@ const ( SessionEventTypeSystemMessage = rpc.SessionEventTypeSystemMessage SessionEventTypeSystemNotification = rpc.SessionEventTypeSystemNotification SessionEventTypeToolExecutionComplete = rpc.SessionEventTypeToolExecutionComplete - SessionEventTypeToolExecutionPartialResult = rpc.SessionEventTypeToolExecutionPartialResult - SessionEventTypeToolExecutionProgress = rpc.SessionEventTypeToolExecutionProgress - SessionEventTypeToolExecutionStart = rpc.SessionEventTypeToolExecutionStart - SessionEventTypeToolSearchActivated = rpc.SessionEventTypeToolSearchActivated - SessionEventTypeToolUserRequested = rpc.SessionEventTypeToolUserRequested - SessionEventTypeUIEphemeralQuery = rpc.SessionEventTypeUIEphemeralQuery - SessionEventTypeUserInputCompleted = rpc.SessionEventTypeUserInputCompleted - SessionEventTypeUserInputRequested = rpc.SessionEventTypeUserInputRequested - SessionEventTypeUserMessage = rpc.SessionEventTypeUserMessage - SessionEventTypeWorkflowRunSettled = rpc.SessionEventTypeWorkflowRunSettled - SessionEventTypeWorkflowRunStarted = rpc.SessionEventTypeWorkflowRunStarted - SessionEventTypeWorkflowRunUpdated = rpc.SessionEventTypeWorkflowRunUpdated - SessionLimitsExhaustedResponseActionAdd = rpc.SessionLimitsExhaustedResponseActionAdd - SessionLimitsExhaustedResponseActionCancel = rpc.SessionLimitsExhaustedResponseActionCancel - SessionLimitsExhaustedResponseActionSet = rpc.SessionLimitsExhaustedResponseActionSet - SessionLimitsExhaustedResponseActionUnset = rpc.SessionLimitsExhaustedResponseActionUnset - SessionModeAutopilot = rpc.SessionModeAutopilot - SessionModeInteractive = rpc.SessionModeInteractive - SessionModePlan = rpc.SessionModePlan - ShutdownTypeError = rpc.ShutdownTypeError - ShutdownTypeRoutine = rpc.ShutdownTypeRoutine - SkillInvokedTriggerAgentInvoked = rpc.SkillInvokedTriggerAgentInvoked - SkillInvokedTriggerContextLoad = rpc.SkillInvokedTriggerContextLoad - SkillInvokedTriggerUserInvoked = rpc.SkillInvokedTriggerUserInvoked - SkillSourceBuiltin = rpc.SkillSourceBuiltin - SkillSourceCustom = rpc.SkillSourceCustom - SkillSourceInherited = rpc.SkillSourceInherited - SkillSourcePersonalAgents = rpc.SkillSourcePersonalAgents - SkillSourcePersonalCopilot = rpc.SkillSourcePersonalCopilot - SkillSourcePlugin = rpc.SkillSourcePlugin - SkillSourceProject = rpc.SkillSourceProject - SkillSourceSDK = rpc.SkillSourceSDK - SubagentModelSelectionSourceAgentDefinitionDefault = rpc.SubagentModelSelectionSourceAgentDefinitionDefault - SubagentModelSelectionSourceComplementaryDefault = rpc.SubagentModelSelectionSourceComplementaryDefault - SubagentModelSelectionSourceConfiguredPreference = rpc.SubagentModelSelectionSourceConfiguredPreference - SubagentModelSelectionSourceConfiguredRequired = rpc.SubagentModelSelectionSourceConfiguredRequired - SubagentModelSelectionSourceExplicitOverride = rpc.SubagentModelSelectionSourceExplicitOverride - SubagentModelSelectionSourceRuntimePolicy = rpc.SubagentModelSelectionSourceRuntimePolicy - SubagentModelSelectionSourceSessionInheritance = rpc.SubagentModelSelectionSourceSessionInheritance - SubagentTaskModelSourceCustomAgentDefinition = rpc.SubagentTaskModelSourceCustomAgentDefinition - SubagentTaskModelSourceSubagentConfiguration = rpc.SubagentTaskModelSourceSubagentConfiguration - SubagentTaskModelSourceTaskArgument = rpc.SubagentTaskModelSourceTaskArgument - SubagentTaskModelSourceUnset = rpc.SubagentTaskModelSourceUnset - SystemMessageRoleDeveloper = rpc.SystemMessageRoleDeveloper - SystemMessageRoleSystem = rpc.SystemMessageRoleSystem - SystemNotificationAgentCompletedStatusCompleted = rpc.SystemNotificationAgentCompletedStatusCompleted - SystemNotificationAgentCompletedStatusFailed = rpc.SystemNotificationAgentCompletedStatusFailed - SystemNotificationTypeAgentCompleted = rpc.SystemNotificationTypeAgentCompleted - SystemNotificationTypeAgentIdle = rpc.SystemNotificationTypeAgentIdle - SystemNotificationTypeInstructionDiscovered = rpc.SystemNotificationTypeInstructionDiscovered - SystemNotificationTypeNewInboxMessage = rpc.SystemNotificationTypeNewInboxMessage - SystemNotificationTypeShellCompleted = rpc.SystemNotificationTypeShellCompleted - SystemNotificationTypeShellDetachedCompleted = rpc.SystemNotificationTypeShellDetachedCompleted - SystemNotificationTypeUnclassified = rpc.SystemNotificationTypeUnclassified - SystemNotificationTypeWorkflowCompleted = rpc.SystemNotificationTypeWorkflowCompleted - SystemNotificationWorkflowCompletedStatusCancelled = rpc.SystemNotificationWorkflowCompletedStatusCancelled - SystemNotificationWorkflowCompletedStatusCompleted = rpc.SystemNotificationWorkflowCompletedStatusCompleted - SystemNotificationWorkflowCompletedStatusError = rpc.SystemNotificationWorkflowCompletedStatusError - SystemNotificationWorkflowCompletedStatusHalted = rpc.SystemNotificationWorkflowCompletedStatusHalted - SystemNotificationWorkflowCompletedStatusPaused = rpc.SystemNotificationWorkflowCompletedStatusPaused - SystemNotificationWorkflowPauseInfoTypeCheckpoint = rpc.SystemNotificationWorkflowPauseInfoTypeCheckpoint - SystemNotificationWorkflowPauseInfoTypeUser = rpc.SystemNotificationWorkflowPauseInfoTypeUser - TaskBlockerKindPermissionRecovery = rpc.TaskBlockerKindPermissionRecovery - TaskCompletionOutcomeBlocked = rpc.TaskCompletionOutcomeBlocked - TaskCompletionOutcomeCompleted = rpc.TaskCompletionOutcomeCompleted - TaskCompletionOutcomeContinue = rpc.TaskCompletionOutcomeContinue - ToolExecutionCompleteContentResourceLinkIconThemeDark = rpc.ToolExecutionCompleteContentResourceLinkIconThemeDark - ToolExecutionCompleteContentResourceLinkIconThemeLight = rpc.ToolExecutionCompleteContentResourceLinkIconThemeLight - ToolExecutionCompleteContentTypeAudio = rpc.ToolExecutionCompleteContentTypeAudio - ToolExecutionCompleteContentTypeImage = rpc.ToolExecutionCompleteContentTypeImage - ToolExecutionCompleteContentTypeResource = rpc.ToolExecutionCompleteContentTypeResource - ToolExecutionCompleteContentTypeResourceLink = rpc.ToolExecutionCompleteContentTypeResourceLink - ToolExecutionCompleteContentTypeShellExit = rpc.ToolExecutionCompleteContentTypeShellExit - ToolExecutionCompleteContentTypeTerminal = rpc.ToolExecutionCompleteContentTypeTerminal - ToolExecutionCompleteContentTypeText = rpc.ToolExecutionCompleteContentTypeText - ToolExecutionCompleteToolDescriptionMetaUIVisibilityApp = rpc.ToolExecutionCompleteToolDescriptionMetaUIVisibilityApp - ToolExecutionCompleteToolDescriptionMetaUIVisibilityModel = rpc.ToolExecutionCompleteToolDescriptionMetaUIVisibilityModel - ToolExecutionStartToolDescriptionMetaUIVisibilityApp = rpc.ToolExecutionStartToolDescriptionMetaUIVisibilityApp - ToolExecutionStartToolDescriptionMetaUIVisibilityModel = rpc.ToolExecutionStartToolDescriptionMetaUIVisibilityModel - UIEphemeralQueryPhaseAborted = rpc.UIEphemeralQueryPhaseAborted - UIEphemeralQueryPhaseChunk = rpc.UIEphemeralQueryPhaseChunk - UIEphemeralQueryPhaseCompleted = rpc.UIEphemeralQueryPhaseCompleted - UIEphemeralQueryPhaseFailed = rpc.UIEphemeralQueryPhaseFailed - UIEphemeralQueryPhaseStarted = rpc.UIEphemeralQueryPhaseStarted - UserMessageAgentModeAutopilot = rpc.UserMessageAgentModeAutopilot - UserMessageAgentModeInteractive = rpc.UserMessageAgentModeInteractive - UserMessageAgentModePlan = rpc.UserMessageAgentModePlan - UserMessageAgentModeShell = rpc.UserMessageAgentModeShell - UserMessageDeliveryIdle = rpc.UserMessageDeliveryIdle - UserMessageDeliveryQueued = rpc.UserMessageDeliveryQueued - UserMessageDeliverySteering = rpc.UserMessageDeliverySteering - UserToolSessionApprovalKindCommands = rpc.UserToolSessionApprovalKindCommands - UserToolSessionApprovalKindCustomTool = rpc.UserToolSessionApprovalKindCustomTool - UserToolSessionApprovalKindExtensionEnvAccess = rpc.UserToolSessionApprovalKindExtensionEnvAccess - UserToolSessionApprovalKindExtensionManagement = rpc.UserToolSessionApprovalKindExtensionManagement - UserToolSessionApprovalKindExtensionPermissionAccess = rpc.UserToolSessionApprovalKindExtensionPermissionAccess - UserToolSessionApprovalKindMCP = rpc.UserToolSessionApprovalKindMCP - UserToolSessionApprovalKindMemory = rpc.UserToolSessionApprovalKindMemory - UserToolSessionApprovalKindRead = rpc.UserToolSessionApprovalKindRead - UserToolSessionApprovalKindWorkflow = rpc.UserToolSessionApprovalKindWorkflow - UserToolSessionApprovalKindWrite = rpc.UserToolSessionApprovalKindWrite - VerbosityHigh = rpc.VerbosityHigh - VerbosityLow = rpc.VerbosityLow - VerbosityMedium = rpc.VerbosityMedium - WorkflowPermissionOperationAuthor = rpc.WorkflowPermissionOperationAuthor - WorkflowPermissionOperationRun = rpc.WorkflowPermissionOperationRun - WorkflowRunSettledStatusCancelled = rpc.WorkflowRunSettledStatusCancelled - WorkflowRunSettledStatusCompleted = rpc.WorkflowRunSettledStatusCompleted - WorkflowRunSettledStatusError = rpc.WorkflowRunSettledStatusError - WorkflowRunSettledStatusHalted = rpc.WorkflowRunSettledStatusHalted - WorkflowRunSettledStatusPaused = rpc.WorkflowRunSettledStatusPaused - WorkingDirectoryContextHostTypeADO = rpc.WorkingDirectoryContextHostTypeADO - WorkingDirectoryContextHostTypeGitHub = rpc.WorkingDirectoryContextHostTypeGitHub - WorkspaceFileChangedOperationCreate = rpc.WorkspaceFileChangedOperationCreate - WorkspaceFileChangedOperationUpdate = rpc.WorkspaceFileChangedOperationUpdate + // Deprecated: SessionEventTypeToolExecutionPartialResult identifies a deprecated event. + SessionEventTypeToolExecutionPartialResult = rpc.SessionEventTypeToolExecutionPartialResult + SessionEventTypeToolExecutionProgress = rpc.SessionEventTypeToolExecutionProgress + SessionEventTypeToolExecutionStart = rpc.SessionEventTypeToolExecutionStart + SessionEventTypeToolSearchActivated = rpc.SessionEventTypeToolSearchActivated + SessionEventTypeToolShellOutput = rpc.SessionEventTypeToolShellOutput + SessionEventTypeToolUserRequested = rpc.SessionEventTypeToolUserRequested + SessionEventTypeUIEphemeralQuery = rpc.SessionEventTypeUIEphemeralQuery + SessionEventTypeUserInputCompleted = rpc.SessionEventTypeUserInputCompleted + SessionEventTypeUserInputRequested = rpc.SessionEventTypeUserInputRequested + SessionEventTypeUserMessage = rpc.SessionEventTypeUserMessage + SessionEventTypeWorkflowRunSettled = rpc.SessionEventTypeWorkflowRunSettled + SessionEventTypeWorkflowRunStarted = rpc.SessionEventTypeWorkflowRunStarted + SessionEventTypeWorkflowRunUpdated = rpc.SessionEventTypeWorkflowRunUpdated + SessionLimitsExhaustedResponseActionAdd = rpc.SessionLimitsExhaustedResponseActionAdd + SessionLimitsExhaustedResponseActionCancel = rpc.SessionLimitsExhaustedResponseActionCancel + SessionLimitsExhaustedResponseActionSet = rpc.SessionLimitsExhaustedResponseActionSet + SessionLimitsExhaustedResponseActionUnset = rpc.SessionLimitsExhaustedResponseActionUnset + SessionModeAutopilot = rpc.SessionModeAutopilot + SessionModeInteractive = rpc.SessionModeInteractive + SessionModePlan = rpc.SessionModePlan + ShutdownTypeError = rpc.ShutdownTypeError + ShutdownTypeRoutine = rpc.ShutdownTypeRoutine + SkillInvokedTriggerAgentInvoked = rpc.SkillInvokedTriggerAgentInvoked + SkillInvokedTriggerContextLoad = rpc.SkillInvokedTriggerContextLoad + SkillInvokedTriggerUserInvoked = rpc.SkillInvokedTriggerUserInvoked + SkillSourceBuiltin = rpc.SkillSourceBuiltin + SkillSourceCustom = rpc.SkillSourceCustom + SkillSourceInherited = rpc.SkillSourceInherited + SkillSourcePersonalAgents = rpc.SkillSourcePersonalAgents + SkillSourcePersonalCopilot = rpc.SkillSourcePersonalCopilot + SkillSourcePlugin = rpc.SkillSourcePlugin + SkillSourceProject = rpc.SkillSourceProject + SkillSourceSDK = rpc.SkillSourceSDK + SubagentModelSelectionSourceAgentDefinitionDefault = rpc.SubagentModelSelectionSourceAgentDefinitionDefault + SubagentModelSelectionSourceComplementaryDefault = rpc.SubagentModelSelectionSourceComplementaryDefault + SubagentModelSelectionSourceConfiguredPreference = rpc.SubagentModelSelectionSourceConfiguredPreference + SubagentModelSelectionSourceConfiguredRequired = rpc.SubagentModelSelectionSourceConfiguredRequired + SubagentModelSelectionSourceExplicitOverride = rpc.SubagentModelSelectionSourceExplicitOverride + SubagentModelSelectionSourceRuntimePolicy = rpc.SubagentModelSelectionSourceRuntimePolicy + SubagentModelSelectionSourceSessionInheritance = rpc.SubagentModelSelectionSourceSessionInheritance + SubagentTaskModelSourceCustomAgentDefinition = rpc.SubagentTaskModelSourceCustomAgentDefinition + SubagentTaskModelSourceSubagentConfiguration = rpc.SubagentTaskModelSourceSubagentConfiguration + SubagentTaskModelSourceTaskArgument = rpc.SubagentTaskModelSourceTaskArgument + SubagentTaskModelSourceUnset = rpc.SubagentTaskModelSourceUnset + SystemMessageRoleDeveloper = rpc.SystemMessageRoleDeveloper + SystemMessageRoleSystem = rpc.SystemMessageRoleSystem + SystemNotificationAgentCompletedStatusCompleted = rpc.SystemNotificationAgentCompletedStatusCompleted + SystemNotificationAgentCompletedStatusFailed = rpc.SystemNotificationAgentCompletedStatusFailed + SystemNotificationTypeAgentCompleted = rpc.SystemNotificationTypeAgentCompleted + SystemNotificationTypeAgentIdle = rpc.SystemNotificationTypeAgentIdle + SystemNotificationTypeInstructionDiscovered = rpc.SystemNotificationTypeInstructionDiscovered + SystemNotificationTypeNewInboxMessage = rpc.SystemNotificationTypeNewInboxMessage + SystemNotificationTypeShellCompleted = rpc.SystemNotificationTypeShellCompleted + SystemNotificationTypeShellDetachedCompleted = rpc.SystemNotificationTypeShellDetachedCompleted + SystemNotificationTypeUnclassified = rpc.SystemNotificationTypeUnclassified + SystemNotificationTypeWorkflowCompleted = rpc.SystemNotificationTypeWorkflowCompleted + SystemNotificationWorkflowCompletedStatusCancelled = rpc.SystemNotificationWorkflowCompletedStatusCancelled + SystemNotificationWorkflowCompletedStatusCompleted = rpc.SystemNotificationWorkflowCompletedStatusCompleted + SystemNotificationWorkflowCompletedStatusError = rpc.SystemNotificationWorkflowCompletedStatusError + SystemNotificationWorkflowCompletedStatusHalted = rpc.SystemNotificationWorkflowCompletedStatusHalted + SystemNotificationWorkflowCompletedStatusPaused = rpc.SystemNotificationWorkflowCompletedStatusPaused + SystemNotificationWorkflowPauseInfoTypeCheckpoint = rpc.SystemNotificationWorkflowPauseInfoTypeCheckpoint + SystemNotificationWorkflowPauseInfoTypeUser = rpc.SystemNotificationWorkflowPauseInfoTypeUser + TaskBlockerKindPermissionRecovery = rpc.TaskBlockerKindPermissionRecovery + TaskCompletionOutcomeBlocked = rpc.TaskCompletionOutcomeBlocked + TaskCompletionOutcomeCompleted = rpc.TaskCompletionOutcomeCompleted + TaskCompletionOutcomeContinue = rpc.TaskCompletionOutcomeContinue + ToolExecutionCompleteContentResourceLinkIconThemeDark = rpc.ToolExecutionCompleteContentResourceLinkIconThemeDark + ToolExecutionCompleteContentResourceLinkIconThemeLight = rpc.ToolExecutionCompleteContentResourceLinkIconThemeLight + ToolExecutionCompleteContentTypeAudio = rpc.ToolExecutionCompleteContentTypeAudio + ToolExecutionCompleteContentTypeImage = rpc.ToolExecutionCompleteContentTypeImage + ToolExecutionCompleteContentTypeResource = rpc.ToolExecutionCompleteContentTypeResource + ToolExecutionCompleteContentTypeResourceLink = rpc.ToolExecutionCompleteContentTypeResourceLink + ToolExecutionCompleteContentTypeShellExit = rpc.ToolExecutionCompleteContentTypeShellExit + ToolExecutionCompleteContentTypeTerminal = rpc.ToolExecutionCompleteContentTypeTerminal + ToolExecutionCompleteContentTypeText = rpc.ToolExecutionCompleteContentTypeText + ToolExecutionCompleteFileEditKindCreate = rpc.ToolExecutionCompleteFileEditKindCreate + ToolExecutionCompleteFileEditKindDelete = rpc.ToolExecutionCompleteFileEditKindDelete + ToolExecutionCompleteFileEditKindEdit = rpc.ToolExecutionCompleteFileEditKindEdit + ToolExecutionCompleteToolDescriptionMetaUIVisibilityApp = rpc.ToolExecutionCompleteToolDescriptionMetaUIVisibilityApp + ToolExecutionCompleteToolDescriptionMetaUIVisibilityModel = rpc.ToolExecutionCompleteToolDescriptionMetaUIVisibilityModel + ToolExecutionStartToolDescriptionMetaUIVisibilityApp = rpc.ToolExecutionStartToolDescriptionMetaUIVisibilityApp + ToolExecutionStartToolDescriptionMetaUIVisibilityModel = rpc.ToolExecutionStartToolDescriptionMetaUIVisibilityModel + ToolShellOutputStreamStderr = rpc.ToolShellOutputStreamStderr + ToolShellOutputStreamStdout = rpc.ToolShellOutputStreamStdout + ToolShellOutputStreamTerminal = rpc.ToolShellOutputStreamTerminal + UIEphemeralQueryPhaseAborted = rpc.UIEphemeralQueryPhaseAborted + UIEphemeralQueryPhaseChunk = rpc.UIEphemeralQueryPhaseChunk + UIEphemeralQueryPhaseCompleted = rpc.UIEphemeralQueryPhaseCompleted + UIEphemeralQueryPhaseFailed = rpc.UIEphemeralQueryPhaseFailed + UIEphemeralQueryPhaseStarted = rpc.UIEphemeralQueryPhaseStarted + UserMessageAgentModeAutopilot = rpc.UserMessageAgentModeAutopilot + UserMessageAgentModeInteractive = rpc.UserMessageAgentModeInteractive + UserMessageAgentModePlan = rpc.UserMessageAgentModePlan + UserMessageAgentModeShell = rpc.UserMessageAgentModeShell + UserMessageDeliveryIdle = rpc.UserMessageDeliveryIdle + UserMessageDeliveryQueued = rpc.UserMessageDeliveryQueued + UserMessageDeliverySteering = rpc.UserMessageDeliverySteering + UserToolSessionApprovalKindCommands = rpc.UserToolSessionApprovalKindCommands + UserToolSessionApprovalKindCustomTool = rpc.UserToolSessionApprovalKindCustomTool + UserToolSessionApprovalKindExtensionEnvAccess = rpc.UserToolSessionApprovalKindExtensionEnvAccess + UserToolSessionApprovalKindExtensionManagement = rpc.UserToolSessionApprovalKindExtensionManagement + UserToolSessionApprovalKindExtensionPermissionAccess = rpc.UserToolSessionApprovalKindExtensionPermissionAccess + UserToolSessionApprovalKindMCP = rpc.UserToolSessionApprovalKindMCP + UserToolSessionApprovalKindMemory = rpc.UserToolSessionApprovalKindMemory + UserToolSessionApprovalKindRead = rpc.UserToolSessionApprovalKindRead + UserToolSessionApprovalKindWorkflow = rpc.UserToolSessionApprovalKindWorkflow + UserToolSessionApprovalKindWrite = rpc.UserToolSessionApprovalKindWrite + VerbosityHigh = rpc.VerbosityHigh + VerbosityLow = rpc.VerbosityLow + VerbosityMedium = rpc.VerbosityMedium + WorkflowPermissionOperationAuthor = rpc.WorkflowPermissionOperationAuthor + WorkflowPermissionOperationRun = rpc.WorkflowPermissionOperationRun + WorkflowRunSettledStatusCancelled = rpc.WorkflowRunSettledStatusCancelled + WorkflowRunSettledStatusCompleted = rpc.WorkflowRunSettledStatusCompleted + WorkflowRunSettledStatusError = rpc.WorkflowRunSettledStatusError + WorkflowRunSettledStatusHalted = rpc.WorkflowRunSettledStatusHalted + WorkflowRunSettledStatusPaused = rpc.WorkflowRunSettledStatusPaused + WorkingDirectoryContextHostTypeADO = rpc.WorkingDirectoryContextHostTypeADO + WorkingDirectoryContextHostTypeGitHub = rpc.WorkingDirectoryContextHostTypeGitHub + WorkspaceFileChangedOperationCreate = rpc.WorkspaceFileChangedOperationCreate + WorkspaceFileChangedOperationUpdate = rpc.WorkspaceFileChangedOperationUpdate ) diff --git a/java/AGENTS.md b/java/AGENTS.md index 684dcdb541..45b9fb9709 100644 --- a/java/AGENTS.md +++ b/java/AGENTS.md @@ -66,7 +66,7 @@ Add new Java E2E tests for Java SDK surface behavior, not shared runtime functionality; new SDK-accessible runtime E2Es belong in `../nodejs/test/e2e/`. For replay-backed Java integration tests, use the on-demand [`sdk-java-e2e-test` skill](../.github/skills/sdk-java-e2e-test/SKILL.md). -Its snapshot workflow and companion examples are not required for unrelated +Its snapshot workflow is not required for unrelated Java edits. For JDK 17 compatibility testing, run the JDK 25-built artifact on JDK 17 without recompiling it, following [Development Setup](README.md#development-setup). diff --git a/java/README.md b/java/README.md index 001383b9c6..f8c0605b90 100644 --- a/java/README.md +++ b/java/README.md @@ -15,6 +15,8 @@ Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build AI-powered applications and agentic workflows. The Java SDK tracks the official GitHub Copilot SDK family (TypeScript, Python, Go, .NET, and Rust). +The generated `SessionFsSetProviderCapabilities.binary` field describes the runtime protocol, not a Java provider API. The Java SDK cannot currently register a session filesystem provider or handle `sessionFs.readFileBytes` or `sessionFs.writeFileBytes`; do not advertise binary operations from Java. Java provider and binary support is deferred until the SDK exposes provider registration and reverse-RPC dispatch. Provider-only images currently require a provider-capable SDK. + ## Prerequisites To use the SDK, you'll need: @@ -74,7 +76,7 @@ implementation 'com.github:copilot-sdk-java:1.0.15-preview.1-SNAPSHOT' ## In-process mode (experimental) -The SDK supports running the Copilot runtime **in-process** as a native library instead of spawning a separate CLI process. This eliminates process management overhead and simplifies deployment. In-process mode is currently experimental and supported on **linux-x64** (glibc), **linux-arm64** (glibc), **linuxmusl-x64**, **win32-x64**, **win32-arm64**, **darwin-x64**, and **darwin-arm64**. +The SDK supports running the Copilot runtime **in-process** as a native library instead of spawning a separate CLI process. This eliminates process management overhead and simplifies deployment. In-process mode is currently experimental and supported on **linux-x64** (glibc), **linux-arm64** (glibc), **linuxmusl-x64**, **linuxmusl-arm64**, **win32-x64**, **win32-arm64**, **darwin-x64**, and **darwin-arm64**. Because in-process mode is experimental, see the [Using experimental APIs](#using-experimental-apis) section for how to opt in. @@ -97,7 +99,7 @@ Add both the SDK and the platform-specific native runtime to your project: ${copilot.version} linux-x64 - + net.java.dev.jna @@ -129,7 +131,12 @@ instead, or configure both transports. GitHub environment name and compute ID ar required; there is no implicit local listener. The host's `getUrl()`, `getToken()`, and `getPid()` may return `null`; `getEnvironmentId()` returns the GitHub environment ID when configured. Environment list/get/delete operations are available only -through the generated RPC API. +through the generated RPC API. Call `host.listSessions()` to read the complete +advertised catalog of live and dormant sessions. +Use `AhpHostOptions.setComputeId(...)` to reuse the durable catalog across local +and Mission Control hosting; it must agree with the GitHub environment's compute +ID when both are supplied. The runtime persists an omitted local-only identity. +Publication survives host shutdown; dormant application sessions require your resume callback. See [runtime-supervised AHP hosting](../docs/runtime-supervised-host.md) for creation and resume callbacks, resident-session publication, ownership, and shared-snapshot E2Es. @@ -181,6 +188,24 @@ public class CopilotSDK { } ``` +`client.forceStop()` retires tracked sessions locally without `session.detach` +RPCs. Retained session objects reject new work and subscriptions, unused handler +captures are released, and pending plain and structured response waits fail. +Already-admitted event handlers and response continuations are not joined. +Forced retirement also drops callbacks when a graceful `session.close()` is +already awaiting detach. Graceful close fails pending response waits without +joining their continuations; ordinary callbacks remain active until detach completes. +Plain response waits preserve terminal results and errors already received before +retirement, even when a blocked continuation delays forwarding them to the caller. +Graceful owned-process shutdown over stdio waits for the runtime transport and +launcher to exit. Over TCP, it closes the connection and terminates the owned +process after the shutdown acknowledgment, without waiting for peer EOF. +Both preserve intentionally detached services; failed or forced shutdown retains +process-tree cleanup. +For a custom TCP launcher that spawns the runtime as a child, graceful cleanup +terminates only the launcher. The launcher must supervise and reap its runtime +child; a shutdown acknowledgment alone does not prove that child exited. + When targeting MCP tools configured through `setMcpServers(...)`, remember the runtime tool name is `-`. For `setAvailableTools(...)` and `setExcludedTools(...)`, prefer the source-qualified filter form @@ -190,6 +215,38 @@ directly. `CopilotClientOptions.setCwd(...)` sets the runtime process working directory, which otherwise inherits the current process working directory. `SessionConfig.setWorkingDirectory(...)` sets the session working directory, which otherwise defaults to the runtime process working directory. +### Subagent lifecycle hooks + +Register `setOnSubagentStart` and `setOnSubagentStop` on `SessionHooks` to inspect +the parent session and subagent metadata around each child run: + +```java +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.SessionConfig; +import com.github.copilot.rpc.SessionHooks; +import com.github.copilot.rpc.SubagentStartHookOutput; +import com.github.copilot.rpc.SubagentStopHookOutput; +import java.util.concurrent.CompletableFuture; + +var hooks = new SessionHooks() + .setOnSubagentStart((input, invocation) -> + CompletableFuture.completedFuture( + new SubagentStartHookOutput("Check the relevant tests first."))) + .setOnSubagentStop((input, invocation) -> + CompletableFuture.completedFuture( + new SubagentStopHookOutput(null, null, input.response()))); +var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setHooks(hooks); +``` + +`SubagentStartHookInput` includes the parent `sessionId`, `timestamp`, `cwd`, +`transcriptPath`, `agentName`, and optional display name and description. Its +`additionalContext` output is prepended to the child's first prompt. +`SubagentStopHookInput` also includes `agentType`, optional `agentId`, +`stopReason`, and the last assistant `response`. Return a stop output with +`decision` set to `"block"` and a nonempty `reason` to request another child +turn, or set `modifiedResponse` to replace the final response. A block takes +precedence over a rewrite. + `CopilotClientOptions.setExtensionLaunchProvider(...)` configures an experimental connection-level resolver for extension launch profiles. The client installs the reverse-RPC handler and registers the provider during startup before sessions can @@ -253,6 +310,51 @@ provider errors, and invalid token responses reject that operation instead of falling back to ambient authentication. Idle sessions refresh only before their next credential-consuming operation; there is no background refresh timer. +### Skill providers (experimental) + +`SessionConfig.setSkillProvider(...)` registers session-scoped skills that the +runtime can list and read on demand. The API is experimental; see +[Using experimental APIs](#using-experimental-apis) before compiling code that +references it. + +```java +import com.github.copilot.SkillProvider; +import com.github.copilot.SkillProviderDescriptor; + +SkillProvider provider = new SkillProvider() { + @Override + public CompletableFuture> listSkills() { + return CompletableFuture.completedFuture(List.of( + new SkillProviderDescriptor("team-plan", "Team planning guidance", true, false, null))); + } + + @Override + public CompletableFuture readSkill(String name) { + if (!name.equals("team-plan")) { + return CompletableFuture.completedFuture(null); // not found + } + return CompletableFuture.completedFuture("# Team plan\nWrite a concise monthly team plan."); + } +}; + +var config = new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setEnableSkills(true) + .setSkillProvider(provider); +``` + +Provider callbacks may be invoked concurrently, so implementations should be +thread-safe. When the runtime cancels a call, for example after its 30-second +limit or when the session disconnects, the SDK calls `cancel(true)` on the +returned future; check `isCancelled()` or attach a completion callback to stop +early. The provider is not persisted with the session; pass it again via +`ResumeSessionConfig.setSkillProvider(...)` on every resume. Resuming without a +provider unbinds any provider the session had. In +`CopilotClientMode.EMPTY`, explicitly set `setEnableSkills(true)` if the +assistant should use skills, because empty mode disables skill loading by +default. Skill providers are local-session only and are rejected for cloud +sessions. + ### Typed MCP installation and removal payloads (breaking change) Three payloads in the experimental MCP installation and removal workflow are now sealed @@ -265,6 +367,12 @@ with the other SDKs. No other generated type changes. | `McpInstallPlan.transportChoices()` | `List` | `List` (`McpPlanTransportChoicePackage` / `McpPlanTransportChoiceRemote`, by `installMethod`) | | `McpInstallationManagementResultOutcome.getOutcome()` / `setOutcome(...)` | `Object` | `McpInstallationManagementOutcome`, whose operation variant carries `McpInstallationOperationStatus` (by `phase`) | +## System Message Customization + +Use `SessionConfig.setSystemMessage(...)` with a `SystemMessageConfig` in `SystemMessageMode.CUSTOMIZE` to override individual prompt sections. Section keys are defined by `SystemMessageSections`; use `SectionOverride.setAction(...)` for static overrides or `SectionOverride.setTransform(...)` for a transform callback. + +`SystemMessageSections.LAST_INSTRUCTIONS` (`last_instructions`) includes configured subagent-model guidance when the `task` tool is available. Removing or replacing this section also removes that guidance; a transform callback receives the complete section, including the guidance, and its returned content is authoritative. Append, prepend, and preserve retain their usual section semantics. These overrides change prompt prose only, not configured subagent models, tool availability, or runtime dispatch policy. `SystemMessageSections.RUNTIME_INSTRUCTIONS` is a separate section: removing it does not remove `SystemMessageSections.LAST_INSTRUCTIONS`. + ## Message source Use `MessageSource.SYSTEM` for application-generated system context and @@ -477,6 +585,54 @@ Chain fluent modifiers to set tool options: For design context and decision rationale, see [ADR-006](docs/adr/adr-006-tool-definition-inline.md). +### String-schema `apply_patch` overrides + +An explicit `apply_patch` override can declare `Map.of("type", "string")`. +The model sees a required `input` property, but the runtime restores the scalar +patch text before dispatch. Use `ToolInvocation.getArgumentsAs(String.class)` +to read it; `getArguments()` is for object-shaped arguments. This example +returns trimmed patch text; replace the handler body with your own patch +implementation: + +```java +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import com.github.copilot.rpc.ToolDefinition; + +ToolDefinition applyPatch = ToolDefinition.create("apply_patch", "Apply a patch", Map.of("type", "string"), + invocation -> { + String patch = invocation.getArgumentsAs(String.class); + return CompletableFuture.completedFuture(patch.trim()); + }).overridesBuiltInTool(true); +``` + +Register this definition with `SessionConfig.setTools(...)`. String-schema +`apply_patch` overrides cannot contain JSON Schema references; use an object +schema if references are needed. + +## Changing tools on a live session (experimental) + +`CopilotSession.setTools(List)` replaces the external tools +registered by this Java client without recreating the session: + +```java +session.setTools(List.of( + ToolDefinition.from("lookup_fruit", "Looks up fruit by code", + Param.of(Integer.class, "code", "Fruit code"), + code -> "dragonfruit") +)).get(); +``` + +The list is a complete replacement for this client only. Built-in tools, MCP +tools, plugin tools, and tools registered by other connections are unchanged. +Pass an empty list to remove this client's tools. Handlers switch after the +runtime accepts the replacement; rejected replacements leave the previous +handlers in place. Tool calls already running finish on the handler that started +them. + +For cross-SDK behavior and active-turn caveats, see +[changing tools](../docs/features/changing-tools.md). + ## Auto routing tiers Use `CapiSessionOptions.setAutoTier(...)` to select `AutoTier.EFFICIENCY`, @@ -631,6 +787,11 @@ observers share the failed future. Some SDK APIs are marked as experimental with `@CopilotExperimental`. These APIs may change or be removed in future versions without notice. +An otherwise stable session event can contain experimental properties. Generated +event payload records preserve each property's schema stability marker on its +record component and generated accessor; the enclosing event and its other +properties remain stable. + By default, referencing an experimental API from your code causes a **compile-time error**: ``` @@ -783,7 +944,7 @@ CI enforces both checks. Spotless runs explicitly in CI; `mvn verify` alone does Run native-runtime Maven commands from the `java` directory. Native packaging requires Node.js in addition to JDK 25 and Maven. In a standalone SDK checkout, `copilot-native/scripts/fetch-native.mjs` retrieves the pinned runtime package from the corresponding GitHub release. When the SDK is nested in `copilot-agent-runtime`, it instead stages the same-checkout artifacts from `dist-cli`; run `pnpm run build:cli` from the runtime repository first. -On a native Linux glibc host, Maven activates `native-linux-x64` or `native-linux-arm64` for the matching architecture when `copilot.native.libc=glibc` is set. On a Linux musl x64 host, Maven activates `native-linuxmusl-x64` when `copilot.native.libc=musl` is set. On Windows x64, Windows ARM64, Intel macOS, and Apple Silicon macOS, Maven activates `native-win32-x64`, `native-win32-arm64`, `native-darwin-x64`, or `native-darwin-arm64` automatically. The matching profile validates the host, runs the native script tests, stages the platform package during `generate-resources`, packages the classifier JAR during `package`, and verifies its native contents. +On a native Linux glibc host, Maven activates `native-linux-x64` or `native-linux-arm64` for the matching architecture when `copilot.native.libc=glibc` is set. On a Linux musl host, Maven activates `native-linuxmusl-x64` or `native-linuxmusl-arm64` for the matching architecture when `copilot.native.libc=musl` is set. On Windows x64, Windows ARM64, Intel macOS, and Apple Silicon macOS, Maven activates `native-win32-x64`, `native-win32-arm64`, `native-darwin-x64`, or `native-darwin-arm64` automatically. The matching profile validates the host, runs the native script tests, stages the platform package during `generate-resources`, packages the classifier JAR during `package`, and verifies its native contents. Before opting in, validate that Node.js reports glibc for the build host: @@ -818,14 +979,14 @@ node copilot-native/scripts/validate-native-host.mjs linux-arm64 mvn -Pinprocess clean verify -Dcopilot.native.libc=glibc ``` -The same command validates in-process mode on Linux musl x64: +The same command validates in-process mode on Linux musl x64 or ARM64: ```bash -node copilot-native/scripts/validate-native-host.mjs linuxmusl-x64 +node copilot-native/scripts/validate-native-host.mjs linuxmusl-x64 # Use linuxmusl-arm64 on ARM64 mvn -Pinprocess clean verify -Dcopilot.native.libc=musl ``` -On Linux musl ARM64 and other unsupported hosts, do not set `copilot.native.libc`. A normal build produces only the OS-neutral primary, sources, and Javadoc JARs; it does not run native script tests, download or stage native files, or produce a platform classifier JAR. +On unsupported hosts, do not set `copilot.native.libc`. A normal build produces only the OS-neutral primary, sources, and Javadoc JARs; it does not run native script tests, download or stage native files, or produce a platform classifier JAR. To build only the OS-neutral artifacts on any host, or override the glibc opt-in, disable native download and packaging: diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml index 2c750e4e47..41af2dd6cb 100644 --- a/java/copilot-native/pom.xml +++ b/java/copilot-native/pom.xml @@ -467,6 +467,64 @@ + + native-linuxmusl-arm64 + + + Linux + aarch64 + + + copilot.native.libc + musl + + + + linuxmusl-arm64 + + + + + org.codehaus.mojo + exec-maven-plugin + + + validate-native-host + validate + + + fetch-native + generate-resources + + + test-fetch-native + test + + + + + org.apache.maven.plugins + maven-jar-plugin + + + jar-native + package + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + verify-native-jars + package + + + + + + native-win32-x64 @@ -809,6 +867,68 @@ + + + attach-external-linuxmusl-arm64-classifier + + + copilot.native.external.linuxmusl.arm64.classifier.path + + + + + + org.codehaus.mojo + exec-maven-plugin + + + validate-external-linuxmusl-arm64-classifier + validate + + exec + + + node + + ${project.basedir}/scripts/validate-native-artifact.mjs + classifier + linuxmusl-arm64 + ${copilot.native.external.linuxmusl.arm64.classifier.path} + ${project.build.finalName}-linuxmusl-arm64.jar + ${copilot.sdk.root} + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-external-linuxmusl-arm64-classifier + package + + attach-artifact + + + + + ${copilot.native.external.linuxmusl.arm64.classifier.path} + jar + linuxmusl-arm64 + + + + + + + + + + jna + ${project.build.directory}/consumer-dependencies + + + + ${testExecutionAgentArgs} ${surefire.jvm.args} --add-opens com.github.copilot.java/com.github.copilot.e2e=ALL-UNNAMED - - 2 + 0 ${copilot.tests.dir} ${copilot.sdk.root} @@ -719,6 +732,22 @@ did not produce the multi-release output. Re-build on JDK 25+ and verify the linuxmusl-x64 + + native-linuxmusl-arm64 + + + Linux + aarch64 + + + copilot.native.libc + musl + + + + linuxmusl-arm64 + + native-win32-x64 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AiCreditsStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/AiCreditsStatus.java new file mode 100644 index 0000000000..f6f95a97a2 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AiCreditsStatus.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Whether the accumulated numeric AI-credit subtotal covers the observed calls. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum AiCreditsStatus { + /** The {@code complete} variant. */ + COMPLETE("complete"), + /** The {@code partial} variant. */ + PARTIAL("partial"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"); + + private final String value; + AiCreditsStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static AiCreditsStatus fromValue(String value) { + for (AiCreditsStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown AiCreditsStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseActivityEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseActivityEvent.java index 64d8d58283..832abb6e3d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseActivityEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseActivityEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "assistant.fusion_phase_activity". Experimental content-safe activity signal for a running HydraFusion phase. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class AssistantFusionPhaseActivityEvent extends SessionEvent { public void setData(AssistantFusionPhaseActivityEventData data) { this.data = data; } /** Data payload for {@link AssistantFusionPhaseActivityEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record AssistantFusionPhaseActivityEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseCompletedEvent.java index d317f93a0e..e5d6804b5f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseCompletedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseCompletedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "assistant.fusion_phase_completed". Experimental durable HydraFusion phase output and lossless replay checkpoint. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class AssistantFusionPhaseCompletedEvent extends SessionEvent { public void setData(AssistantFusionPhaseCompletedEventData data) { this.data = data; } /** Data payload for {@link AssistantFusionPhaseCompletedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record AssistantFusionPhaseCompletedEventData( @@ -47,6 +52,7 @@ public record AssistantFusionPhaseCompletedEventData( /** Concrete model that executed the phase. */ @JsonProperty("model") String model, /** Explicit reasoning effort selected for this phase, if supplied. */ + @CopilotExperimental @JsonProperty("reasoningEffort") String reasoningEffort, /** Durable outcome status of the phase. */ @JsonProperty("status") FusionPhaseStatus status, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseFailedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseFailedEvent.java index c222e0e0c3..45b850040c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseFailedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseFailedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "assistant.fusion_phase_failed". Experimental durable typed HydraFusion phase failure and degradation transition. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class AssistantFusionPhaseFailedEvent extends SessionEvent { public void setData(AssistantFusionPhaseFailedEventData data) { this.data = data; } /** Data payload for {@link AssistantFusionPhaseFailedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record AssistantFusionPhaseFailedEventData( @@ -47,6 +52,7 @@ public record AssistantFusionPhaseFailedEventData( /** Concrete model that attempted the phase. */ @JsonProperty("model") String model, /** Explicit reasoning effort selected for this phase, if supplied. */ + @CopilotExperimental @JsonProperty("reasoningEffort") String reasoningEffort, /** Durable outcome status of the phase. */ @JsonProperty("status") FusionPhaseStatus status, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseStartedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseStartedEvent.java index 9408e87c2b..a7efa03b82 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseStartedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantFusionPhaseStartedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "assistant.fusion_phase_started". Experimental transient HydraFusion phase/model/role signal. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class AssistantFusionPhaseStartedEvent extends SessionEvent { public void setData(AssistantFusionPhaseStartedEventData data) { this.data = data; } /** Data payload for {@link AssistantFusionPhaseStartedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record AssistantFusionPhaseStartedEventData( @@ -49,6 +54,7 @@ public record AssistantFusionPhaseStartedEventData( /** Concrete model executing the phase. */ @JsonProperty("model") String model, /** Explicit reasoning effort selected for this phase, if supplied. */ + @CopilotExperimental @JsonProperty("reasoningEffort") String reasoningEffort ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java index d3154846ed..87d0e6f29a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageEvent.java @@ -10,6 +10,7 @@ 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; @@ -41,6 +42,8 @@ public record AssistantMessageEventData( @JsonProperty("originatingMessageId") String originatingMessageId, /** Model that produced this assistant message, if known */ @JsonProperty("model") String model, + /** Opaque origin provider identity captured at dispatch. Only an exact match with the target provider permits replay of reasoningOpaque, encryptedContent, reasoningBlocks, serverTools, and provider response/item identifiers. Matching model ids or wire protocols is insufficient. Omitted for legacy or unattributed messages; the runtime excludes unproven opaque state when projecting to a known provider. Text content and client tool requests/results remain portable semantic history. */ + @JsonProperty("providerId") String providerId, /** The assistant's text response content */ @JsonProperty("content") String content, /** Tool invocations requested by the assistant in this message */ @@ -82,8 +85,10 @@ public record AssistantMessageEventData( /** Tool call ID of the parent tool invocation when this event originates from a sub-agent */ @JsonProperty("parentToolCallId") String parentToolCallId, /** Provider-agnostic citations linking spans of this message's content to the sources that support them. Experimental; only populated when citation emission is enabled. */ + @CopilotExperimental @JsonProperty("citations") Citations citations, /** Experimental HydraFusion source attribution for this ordinary authoritative assistant message. */ + @CopilotExperimental @JsonProperty("fusion") FusionAttribution fusion ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageReasoningBlocks.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageReasoningBlocks.java index 3603e09f1e..e9b97f00f3 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageReasoningBlocks.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantMessageReasoningBlocks.java @@ -29,6 +29,8 @@ public record AssistantMessageReasoningBlocks( /** Model provider that produced these reasoning blocks. */ @JsonProperty("provider") String provider, /** Provider-native reasoning items or content blocks preserved verbatim, in order. A single response can carry several, and provider signatures or identifiers may depend on their exact content and ordering. */ - @JsonProperty("blocks") List blocks + @JsonProperty("blocks") List blocks, + /** Anthropic Messages assistant block ordering preserved when the legacy reasoning-only representation cannot reproduce it exactly. Thinking and text blocks remain verbatim; tool-use entries retain identity and a payload fingerprint when later signed reasoning depends on them, and are hydrated from the message's tool requests during replay. */ + @JsonProperty("orderedBlocks") List orderedBlocks ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java index a2e08bfb14..75eb6f53f0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AssistantUsageEvent.java @@ -10,6 +10,7 @@ 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.time.OffsetDateTime; import java.util.List; import java.util.Map; @@ -37,8 +38,16 @@ public final class AssistantUsageEvent extends SessionEvent { @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record AssistantUsageEventData( + /** Availability of this call's reported AI-credit amount. Missing billing is unavailable, not a zero charge. */ + @JsonProperty("aiCreditsStatus") AiCreditsStatus aiCreditsStatus, /** Model identifier used for this API call */ @JsonProperty("model") String model, + /** Provider identity and product label captured by the actual call's dispatch path. */ + @JsonProperty("provider") ModelProviderRef provider, + /** Model display name captured for this call, not derived from the current selection. */ + @JsonProperty("modelDisplayName") String modelDisplayName, + /** Runtime-owned identity shared with the durable accounting receipt. */ + @JsonProperty("accounting") UsageAccountingIdentity accounting, /** Number of input tokens consumed */ @JsonProperty("inputTokens") Long inputTokens, /** Number of output tokens produced */ @@ -56,6 +65,7 @@ public record AssistantUsageEventData( /** Recognized provider-reported reasons for dropped thinking blocks, in response order */ @JsonProperty("thinkingDroppedReasons") List thinkingDroppedReasons, /** Model multiplier cost for billing purposes */ + @CopilotExperimental @JsonProperty("cost") Double cost, /** Duration of the API call in milliseconds */ @JsonProperty("duration") Long duration, @@ -71,6 +81,10 @@ public record AssistantUsageEventData( @JsonProperty("interactionType") String interactionType, /** Whether this model call used a bring-your-own-key provider */ @JsonProperty("isByok") Boolean isByok, + /** Where the bring-your-own-key model runs and who manages it: "local_managed" (on the device, managed by Copilot), "local_user" (on the device, managed by the user), or "remote_user" (off the device, managed by the user). Absent for Copilot-served models. */ + @JsonProperty("byokKind") String byokKind, + /** Fixed-set provider family serving the bring-your-own-key model (for example "openai", "anthropic", "azure_openai", "ollama", "llama_cpp", or "other"). Never the caller-supplied provider name. Absent for Copilot-served models. */ + @JsonProperty("modelProvider") String modelProvider, /** Whether Auto mode was selected for this model call */ @JsonProperty("isAuto") Boolean isAuto, /** Effective maximum prompt-token limit used for this model call */ @@ -83,6 +97,12 @@ public record AssistantUsageEventData( @JsonProperty("rejectedPredictionTokens") Long rejectedPredictionTokens, /** Transport used for this model call (http or websocket) */ @JsonProperty("transport") AssistantUsageTransport transport, + /** Serialized (uncompressed) byte length of the request body. A content-free size signal. */ + @JsonProperty("requestBodyBytes") Long requestBodyBytes, + /** Why the call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred */ + @JsonProperty("websocketFallbackReason") ModelCallWebSocketFallbackReason websocketFallbackReason, + /** Milliseconds spent on the WebSocket attempt before falling back to HTTP */ + @JsonProperty("websocketFallbackAfterMs") Long websocketFallbackAfterMs, /** Completion ID from the model provider (e.g., chatcmpl-abc123) */ @JsonProperty("apiCallId") String apiCallId, /** GitHub request tracing ID (x-github-request-id header) for server-side log correlation */ @@ -122,6 +142,7 @@ public record AssistantUsageEventData( /** Whether the model response was blocked or truncated by content filtering (finish_reason === 'content_filter'). For Anthropic models this corresponds to a 'refusal' stop reason. */ @JsonProperty("contentFilterTriggered") Boolean contentFilterTriggered, /** Experimental HydraFusion attribution for this concrete model call's usage. */ + @CopilotExperimental @JsonProperty("fusion") FusionAttribution fusion ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/AutoTier.java b/java/sdk/src/generated/java/com/github/copilot/generated/AutoTier.java index 1c9d81b8ed..b3669208d7 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/AutoTier.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/AutoTier.java @@ -10,30 +10,38 @@ import javax.annotation.processing.Generated; /** - * Routing preference used when the session model is `auto`. `fast` is an integrator-only latency preset and is not a first-party GitHub Copilot product preference. + * Extensible routing preference for the virtual `auto` model. New identifiers must be advertised and enabled by the provider. `fast` is an integrator-only latency preset. * * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum AutoTier { +public final class AutoTier { /** The {@code efficiency} variant. */ - EFFICIENCY("efficiency"), + public static final AutoTier EFFICIENCY = new AutoTier("efficiency"); /** The {@code balance} variant. */ - BALANCE("balance"), + public static final AutoTier BALANCE = new AutoTier("balance"); /** The {@code intelligence} variant. */ - INTELLIGENCE("intelligence"), + public static final AutoTier INTELLIGENCE = new AutoTier("intelligence"); /** The {@code fast} variant. */ - FAST("fast"); + public static final AutoTier FAST = new AutoTier("fast"); private final String value; - AutoTier(String value) { this.value = value; } + private AutoTier(String value) { this.value = value; } @com.fasterxml.jackson.annotation.JsonValue public String getValue() { return value; } @com.fasterxml.jackson.annotation.JsonCreator public static AutoTier fromValue(String value) { + if (value == null) return null; + if (value.isEmpty() || value.codePoints().anyMatch(c -> Character.isWhitespace(c) || Character.isISOControl(c))) + throw new IllegalArgumentException("AutoTier requires a routing identifier"); for (AutoTier v : values()) { if (v.value.equals(value)) return v; } - throw new IllegalArgumentException("Unknown AutoTier value: " + value); + return new AutoTier(value); } + /** Returns the known routing preferences. @return known values */ + public static AutoTier[] values() { return new AutoTier[] { EFFICIENCY, BALANCE, INTELLIGENCE, FAST }; } + @Override public boolean equals(Object other) { return other instanceof AutoTier v && value.equals(v.value); } + @Override public int hashCode() { return value.hashCode(); } + @Override public String toString() { return value; } } 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 ed454deafd..c325857e59 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,12 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record CompactionCompleteCompactionTokensUsed( + /** 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. */ + @JsonProperty("provider") ModelProviderRef provider, + /** Model display name captured for the compaction call. */ + @JsonProperty("modelDisplayName") String modelDisplayName, /** Input tokens consumed by the compaction LLM call */ @JsonProperty("inputTokens") Long inputTokens, /** Output tokens produced by the compaction LLM call */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseActor.java b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseActor.java new file mode 100644 index 0000000000..ef66119adf --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseActor.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Controlled provenance for a typed runtime response. Only `human_response`, minted by a trusted direct-interaction ingress, is human authorization evidence. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum HumanResponseActor { + /** The {@code human_response} variant. */ + HUMAN_RESPONSE("human_response"), + /** The {@code host_automation} variant. */ + HOST_AUTOMATION("host_automation"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"); + + private final String value; + HumanResponseActor(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static HumanResponseActor fromValue(String value) { + for (HumanResponseActor v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown HumanResponseActor value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedEvent.java new file mode 100644 index 0000000000..f2a2aa9012 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedEvent.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Session event "human_response.recorded". Durable request-correlated evidence for a typed response to a runtime-owned question or plan review. + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class HumanResponseRecordedEvent extends SessionEvent { + + @Override + public String getType() { return "human_response.recorded"; } + + @JsonProperty("data") + private HumanResponseRecordedEventData data; + + public HumanResponseRecordedEventData getData() { return data; } + public void setData(HumanResponseRecordedEventData data) { this.data = data; } + + /** Data payload for {@link HumanResponseRecordedEvent}. */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record HumanResponseRecordedEventData( + /** Request ID of the runtime-owned question or plan review. */ + @JsonProperty("requestId") String requestId, + /** Tool call ID that opened the request, when present. */ + @JsonProperty("toolCallId") String toolCallId, + /** Controlled actor provenance established at response ingress. */ + @JsonProperty("actor") HumanResponseActor actor, + /** Typed request and response payload. */ + @JsonProperty("response") HumanResponseRecordedResponse response + ) { + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenAcquireResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponse.java similarity index 52% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenAcquireResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponse.java index 582d7036d3..65efb2e2e0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenAcquireResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponse.java @@ -5,35 +5,32 @@ // AUTO-GENERATED FILE - DO NOT EDIT // Generated from: api.schema.json -package com.github.copilot.generated.rpc; +package com.github.copilot.generated; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.github.copilot.CopilotExperimental; import javax.annotation.processing.Generated; /** - * Result of a OneAuth token acquisition. - * - * @apiNote This type is experimental and may change in a future version. + * Exact runtime-owned question or reviewed plan paired with the typed response that settled it. * * @since 1.0.0 */ -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "status", visible = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "responseKind", visible = true) @JsonSubTypes({ - @JsonSubTypes.Type(value = EntraTokenAcquireResultOk.class, name = "ok"), - @JsonSubTypes.Type(value = EntraTokenAcquireResultInteractionRequired.class, name = "interaction-required") + @JsonSubTypes.Type(value = HumanResponseRecordedResponseAskUser.class, name = "ask_user"), + @JsonSubTypes.Type(value = HumanResponseRecordedResponseUserInput.class, name = "user_input"), + @JsonSubTypes.Type(value = HumanResponseRecordedResponseExitPlanMode.class, name = "exit_plan_mode") }) -@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @javax.annotation.processing.Generated("copilot-sdk-codegen") -public abstract class EntraTokenAcquireResult { +public abstract class HumanResponseRecordedResponse { /** * Returns the discriminator value for this variant. * - * @return the status discriminator + * @return the responseKind discriminator */ - public abstract String getStatus(); + public abstract String getResponseKind(); } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponseAskUser.java b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponseAskUser.java new file mode 100644 index 0000000000..03afda0422 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponseAskUser.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; + +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; + +/** + * Variant {@code ask_user} of {@link HumanResponseRecordedResponse}. + * + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class HumanResponseRecordedResponseAskUser extends HumanResponseRecordedResponse { + + @JsonProperty("responseKind") + private final String responseKind = "ask_user"; + + @Override + public String getResponseKind() { return responseKind; } + + /** Exact answer content accepted from the user. */ + @JsonProperty("content") + private Map content; + + /** Exact question displayed to the user. */ + @JsonProperty("message") + private String message; + + /** Exact response schema displayed to the user. */ + @JsonProperty("requestedSchema") + private ElicitationRequestedSchema requestedSchema; + + public Map getContent() { return content; } + public void setContent(Map content) { this.content = content; } + + public String getMessage() { return message; } + public void setMessage(String message) { this.message = message; } + + public ElicitationRequestedSchema getRequestedSchema() { return requestedSchema; } + public void setRequestedSchema(ElicitationRequestedSchema requestedSchema) { this.requestedSchema = requestedSchema; } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponseExitPlanMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponseExitPlanMode.java new file mode 100644 index 0000000000..0d13b119da --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponseExitPlanMode.java @@ -0,0 +1,87 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Variant {@code exit_plan_mode} of {@link HumanResponseRecordedResponse}. + * + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class HumanResponseRecordedResponseExitPlanMode extends HumanResponseRecordedResponse { + + @JsonProperty("responseKind") + private final String responseKind = "exit_plan_mode"; + + @Override + public String getResponseKind() { return responseKind; } + + /** Exact plan summary displayed to the user. */ + @JsonProperty("summary") + private String summary; + + /** Exact full plan content available from the review UI. */ + @JsonProperty("planContent") + private String planContent; + + /** Actions offered by the plan review UI. */ + @JsonProperty("actions") + private List actions; + + /** Action the plan review UI recommended. */ + @JsonProperty("recommendedAction") + private ExitPlanModeAction recommendedAction; + + /** Whether the user approved the reviewed plan. */ + @JsonProperty("approved") + private Boolean approved; + + /** Action selected by the user, when applicable. */ + @JsonProperty("selectedAction") + private ExitPlanModeAction selectedAction; + + /** Whether the selected response requested edit auto-approval. */ + @JsonProperty("autoApproveEdits") + private Boolean autoApproveEdits; + + /** Exact feedback submitted with the plan decision, when present. */ + @JsonProperty("feedback") + private String feedback; + + public String getSummary() { return summary; } + public void setSummary(String summary) { this.summary = summary; } + + public String getPlanContent() { return planContent; } + public void setPlanContent(String planContent) { this.planContent = planContent; } + + public List getActions() { return actions; } + public void setActions(List actions) { this.actions = actions; } + + public ExitPlanModeAction getRecommendedAction() { return recommendedAction; } + public void setRecommendedAction(ExitPlanModeAction recommendedAction) { this.recommendedAction = recommendedAction; } + + public Boolean getApproved() { return approved; } + public void setApproved(Boolean approved) { this.approved = approved; } + + public ExitPlanModeAction getSelectedAction() { return selectedAction; } + public void setSelectedAction(ExitPlanModeAction selectedAction) { this.selectedAction = selectedAction; } + + public Boolean getAutoApproveEdits() { return autoApproveEdits; } + public void setAutoApproveEdits(Boolean autoApproveEdits) { this.autoApproveEdits = autoApproveEdits; } + + public String getFeedback() { return feedback; } + public void setFeedback(String feedback) { this.feedback = feedback; } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponseUserInput.java b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponseUserInput.java new file mode 100644 index 0000000000..2845d6169a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/HumanResponseRecordedResponseUserInput.java @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Variant {@code user_input} of {@link HumanResponseRecordedResponse}. + * + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class HumanResponseRecordedResponseUserInput extends HumanResponseRecordedResponse { + + @JsonProperty("responseKind") + private final String responseKind = "user_input"; + + @Override + public String getResponseKind() { return responseKind; } + + /** Exact question displayed to the user. */ + @JsonProperty("question") + private String question; + + /** Exact choices displayed to the user, when the request offered choices. */ + @JsonProperty("choices") + private List choices; + + /** Whether the displayed request allowed a free-form answer. */ + @JsonProperty("allowFreeform") + private Boolean allowFreeform; + + /** Exact selected or free-form answer submitted by the user. */ + @JsonProperty("answer") + private String answer; + + /** Whether the answer was typed as free-form text rather than selected from the displayed choices. */ + @JsonProperty("wasFreeform") + private Boolean wasFreeform; + + public String getQuestion() { return question; } + public void setQuestion(String question) { this.question = question; } + + public List getChoices() { return choices; } + public void setChoices(List choices) { this.choices = choices; } + + public Boolean getAllowFreeform() { return allowFreeform; } + public void setAllowFreeform(Boolean allowFreeform) { this.allowFreeform = allowFreeform; } + + public String getAnswer() { return answer; } + public void setAnswer(String answer) { this.answer = answer; } + + public Boolean getWasFreeform() { return wasFreeform; } + public void setWasFreeform(Boolean wasFreeform) { this.wasFreeform = wasFreeform; } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ManagedPluginProgressPhase.java b/java/sdk/src/generated/java/com/github/copilot/generated/ManagedPluginProgressPhase.java new file mode 100644 index 0000000000..a1b2f401b5 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ManagedPluginProgressPhase.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Current phase of organization-required plugin preparation. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ManagedPluginProgressPhase { + /** The {@code initializing} variant. */ + INITIALIZING("initializing"), + /** The {@code installing} variant. */ + INSTALLING("installing"), + /** The {@code updating} variant. */ + UPDATING("updating"), + /** The {@code complete} variant. */ + COMPLETE("complete"); + + private final String value; + ManagedPluginProgressPhase(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedPluginProgressPhase fromValue(String value) { + for (ManagedPluginProgressPhase v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedPluginProgressPhase value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedEscalation.java b/java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedEscalation.java index 619fb326e1..ff4f36d055 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedEscalation.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ManagedSettingsEnforcedEscalation.java @@ -10,7 +10,7 @@ import javax.annotation.processing.Generated; /** - * For a `bypass_permissions_blocked` action, which permission-escalation primitive was refused + * For a `bypass_permissions_blocked` action, which permission-mode or escalation primitive was refused * * @since 1.0.0 */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/McpServerSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/McpServerSource.java index 5ea2c39f51..2354c95750 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/McpServerSource.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/McpServerSource.java @@ -10,7 +10,7 @@ import javax.annotation.processing.Generated; /** - * Configuration source: user, workspace, plugin, builtin, or managed + * Configuration source: user, workspace, plugin, builtin, managed, or account * * @since 1.0.0 */ @@ -25,7 +25,9 @@ public enum McpServerSource { /** The {@code builtin} variant. */ BUILTIN("builtin"), /** The {@code managed} variant. */ - MANAGED("managed"); + MANAGED("managed"), + /** The {@code account} variant. */ + ACCOUNT("account"); private final String value; McpServerSource(String value) { this.value = value; } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java index 8e3e166f04..717523a2ee 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureEvent.java @@ -10,6 +10,7 @@ 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; @@ -59,12 +60,24 @@ public record ModelCallFailureEventData( @JsonProperty("transport") ModelCallFailureTransport transport, /** Whether the failure originated from an API response or the request transport */ @JsonProperty("failureKind") ModelCallFailureKind failureKind, + /** Serialized (uncompressed) byte length of the failed request body. A content-free size signal. */ + @JsonProperty("requestBodyBytes") Long requestBodyBytes, + /** Zero-based orchestrator retry index of the failed attempt */ + @JsonProperty("retryAttempt") Long retryAttempt, + /** Why the failed call was carried by the HTTP fallback of a WebSocket-capable dispatcher; absent when no fallback occurred */ + @JsonProperty("websocketFallbackReason") ModelCallWebSocketFallbackReason websocketFallbackReason, + /** Milliseconds spent on the WebSocket attempt before falling back to HTTP */ + @JsonProperty("websocketFallbackAfterMs") Long websocketFallbackAfterMs, /** Effective maximum prompt-token limit for the failed call */ @JsonProperty("maxPromptTokens") Long maxPromptTokens, /** Effective maximum output-token limit for the failed call */ @JsonProperty("maxOutputTokens") Long maxOutputTokens, /** Whether the failed call used a bring-your-own-key provider */ @JsonProperty("isByok") Boolean isByok, + /** Where the bring-your-own-key model for the failed call runs and who manages it: "local_managed" (on the device, managed by Copilot), "local_user" (on the device, managed by the user), or "remote_user" (off the device, managed by the user). Absent for Copilot-served models. */ + @JsonProperty("byokKind") String byokKind, + /** Fixed-set provider family serving the bring-your-own-key model for the failed call (for example "openai", "anthropic", "azure_openai", "ollama", "llama_cpp", or "other"). Never the caller-supplied provider name. Absent for Copilot-served models. */ + @JsonProperty("modelProvider") String modelProvider, /** Whether the session selected Auto mode for the failed call */ @JsonProperty("isAuto") Boolean isAuto, /** Reasoning effort level used for the failed model call, if applicable */ @@ -86,6 +99,7 @@ public record ModelCallFailureEventData( /** Content-free structural summary of the failing request. Contains only counts and shape flags (no prompt content), so it is safe for unrestricted telemetry. Populated only for client-error (4xx) failures. */ @JsonProperty("requestFingerprint") ModelCallFailureRequestFingerprint requestFingerprint, /** Experimental HydraFusion attribution for this failed concrete model call. */ + @CopilotExperimental @JsonProperty("fusion") FusionAttribution fusion ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureRequestFingerprint.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureRequestFingerprint.java index b8d0622b6e..6e83338e11 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureRequestFingerprint.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallFailureRequestFingerprint.java @@ -33,6 +33,12 @@ public record ModelCallFailureRequestFingerprint( @JsonProperty("imagePartCount") Long imagePartCount, /** Image parts whose media type cannot be determined (rejected by strict providers) */ @JsonProperty("imagePartsMissingMediaType") Long imagePartsMissingMediaType, + /** Summed byte length of inline image payloads (data URLs and base64 sources) */ + @JsonProperty("imageBytes") Long imageBytes, + /** Number of messages carrying opaque or encrypted reasoning */ + @JsonProperty("reasoningItemCount") Long reasoningItemCount, + /** Summed byte length of opaque or encrypted reasoning payloads */ + @JsonProperty("encryptedContentBytes") Long encryptedContentBytes, /** Role of the final message in the request */ @JsonProperty("lastMessageRole") String lastMessageRole ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallStartEvent.java index a3db0aadbe..0e76806f41 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallStartEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallStartEvent.java @@ -10,6 +10,7 @@ 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; /** @@ -41,6 +42,7 @@ public record ModelCallStartEventData( /** Previous response or interaction identifier included in the model request, when present */ @JsonProperty("previousResponseId") String previousResponseId, /** Experimental HydraFusion attribution for this concrete model call. */ + @CopilotExperimental @JsonProperty("fusion") FusionAttribution fusion, /** Parent task tool call ID when this model call belongs to a sub-agent */ @JsonProperty("parentToolCallId") String parentToolCallId diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallWebSocketFallbackReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallWebSocketFallbackReason.java new file mode 100644 index 0000000000..fc891ae508 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ModelCallWebSocketFallbackReason.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Why a WebSocket-capable model call was carried by the HTTP fallback + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelCallWebSocketFallbackReason { + /** The {@code connect_failed} variant. */ + CONNECT_FAILED("connect_failed"), + /** The {@code connection_unavailable} variant. */ + CONNECTION_UNAVAILABLE("connection_unavailable"), + /** The {@code send_failed} variant. */ + SEND_FAILED("send_failed"), + /** The {@code api_error} variant. */ + API_ERROR("api_error"), + /** The {@code transport_failed} variant. */ + TRANSPORT_FAILED("transport_failed"); + + private final String value; + ModelCallWebSocketFallbackReason(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelCallWebSocketFallbackReason fromValue(String value) { + for (ModelCallWebSocketFallbackReason v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelCallWebSocketFallbackReason value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ModelProviderKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelProviderKind.java new file mode 100644 index 0000000000..cbef223e2b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ModelProviderKind.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * The neutral kind of a model provider — the model analog of `AccountKind`. A model provider is the live, entitled source a model came from; central code never branches on this beyond a single dispatch. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelProviderKind { + /** The {@code copilot} variant. */ + COPILOT("copilot"), + /** The {@code loki} variant. */ + LOKI("loki"); + + private final String value; + ModelProviderKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelProviderKind fromValue(String value) { + for (ModelProviderKind v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelProviderKind value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ModelProviderRef.java b/java/sdk/src/generated/java/com/github/copilot/generated/ModelProviderRef.java new file mode 100644 index 0000000000..84a0fa8954 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ModelProviderRef.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import 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 neutral reference to the model provider that produced a model: an opaque id, a human-readable label, and the provider kind. Carried on each enumerated Model so consumers can group by provider without reaching into a provider-shaped internal type. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderRef( + /** Opaque, stable id of the provider that produced this model. Matches the enumerated `ModelProviderDescriptor.id`. */ + @JsonProperty("id") String id, + /** Human-readable provider label, owned by the runtime so every consumer renders identical text. */ + @JsonProperty("label") String label, + /** The provider kind. */ + @JsonProperty("kind") ModelProviderKind kind +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionAssentDetectedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionAssentDetectedEvent.java index 0f3453c45a..cd1b9c8c38 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionAssentDetectedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionAssentDetectedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "permission.assentDetected". Historical decode-only contextual-assent marker. Current runtimes do not project it into the conversation or permission flow. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,12 +35,15 @@ public final class PermissionAssentDetectedEvent extends SessionEvent { public void setData(PermissionAssentDetectedEventData data) { this.data = data; } /** Data payload for {@link PermissionAssentDetectedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record PermissionAssentDetectedEventData( /** Permission request the likely assent may refer to. The runtime derives this from the preceding durable blocker; the human message and extraction model do not choose it. */ + @CopilotExperimental @JsonProperty("requestId") String requestId, /** Human turn whose text triggered the deterministic assent recognizer. */ + @CopilotExperimental @JsonProperty("turnIndex") Long turnIndex ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionCarriedForwardEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionCarriedForwardEvent.java index a89b59660d..49061d7f4b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionCarriedForwardEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionCarriedForwardEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "permission.carriedForward". Historical decode-only receipt from the retired Assisted Permissions authorization extractor. Current runtimes ignore it for permission decisions. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,16 +35,21 @@ public final class PermissionCarriedForwardEvent extends SessionEvent { public void setData(PermissionCarriedForwardEventData data) { this.data = data; } /** Data payload for {@link PermissionCarriedForwardEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record PermissionCarriedForwardEventData( /** Authorization edge minted for this admission. Not a prompt id: no prompt was raised, so no client should expect a request with this id. */ + @CopilotExperimental @JsonProperty("requestId") String requestId, /** Tool call this admission authorizes. Its execution receipts the prior grant, which is how a single-effect approval is spent rather than carried forward again. */ + @CopilotExperimental @JsonProperty("toolCallId") String toolCallId, /** Identity of the prior authorization record that contained the proposal. */ + @CopilotExperimental @JsonProperty("recordId") String recordId, /** Always `authorization_carry_forward`. Stated explicitly so a consumer reading this event cannot mistake it for a human, host-policy, or assisted-approval decision. */ + @CopilotExperimental @JsonProperty("decisionSource") PermissionDecisionSource decisionSource ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionCompletedEvent.java index b6da6bfb05..2cafbcafb8 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionCompletedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionCompletedEvent.java @@ -10,6 +10,7 @@ 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; /** @@ -45,6 +46,7 @@ public record PermissionCompletedEventData( /** The result of the permission request */ @JsonProperty("result") Object result, /** Who decided this permission request. Absent on completions recorded before this field existed, which consumers must treat as "not a human decision" rather than assuming one. Authorization records are minted only for `human_response`; an assisted-approval verdict, a host policy, an unattended fallback, and a hook resolution all produce the same `result` a person does, so this is the only field that distinguishes them. */ + @CopilotExperimental @JsonProperty("decisionSource") PermissionDecisionSource decisionSource ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionContextualAuthorizationEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionContextualAuthorizationEvent.java index 0712d39db8..b7eea2dae2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionContextualAuthorizationEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionContextualAuthorizationEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "permission.contextualAuthorization". Historical decode-only contextual authorization claim. Current runtimes preserve the payload but do not establish authority from it. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,20 +35,27 @@ public final class PermissionContextualAuthorizationEvent extends SessionEvent { public void setData(PermissionContextualAuthorizationEventData data) { this.data = data; } /** Data payload for {@link PermissionContextualAuthorizationEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record PermissionContextualAuthorizationEventData( /** Deterministic identity of the contextual message grant. */ + @CopilotExperimental @JsonProperty("recordId") String recordId, /** Original blocked permission request selected by deterministic event ordering, never by the extraction model. */ + @CopilotExperimental @JsonProperty("requestId") String requestId, /** Human turn containing the contextual decision. */ + @CopilotExperimental @JsonProperty("turnIndex") Long turnIndex, /** Whether the contextual human span granted or denied authority. */ + @CopilotExperimental @JsonProperty("polarity") PermissionMessageAuthorizationPolarity polarity, /** Start byte offset of the contextual decision span within the turn. */ + @CopilotExperimental @JsonProperty("spanStart") Long spanStart, /** End byte offset of the contextual decision span within the turn. */ + @CopilotExperimental @JsonProperty("spanEnd") Long spanEnd ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationDegradedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationDegradedEvent.java index 8efdf509a6..f793de8ad2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationDegradedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationDegradedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "permission.messageAuthorizationDegraded". Historical decode-only degradation marker from the retired extractor. Current runtimes ignore it for permission decisions. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,10 +35,12 @@ public final class PermissionMessageAuthorizationDegradedEvent extends SessionEv public void setData(PermissionMessageAuthorizationDegradedEventData data) { this.data = data; } /** Data payload for {@link PermissionMessageAuthorizationDegradedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record PermissionMessageAuthorizationDegradedEventData( /** The human turn that could not be represented safely. */ + @CopilotExperimental @JsonProperty("turnIndex") Long turnIndex ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationEvent.java index 3a6c5aa6f0..57254afaad 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationEvent.java @@ -10,13 +10,17 @@ 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; /** * Session event "permission.messageAuthorization". Historical decode-only claim from the retired Assisted Permissions authorization extractor. Current runtimes preserve the payload but do not establish authority from it. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -32,26 +36,36 @@ public final class PermissionMessageAuthorizationEvent extends SessionEvent { public void setData(PermissionMessageAuthorizationEventData data) { this.data = data; } /** Data payload for {@link PermissionMessageAuthorizationEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record PermissionMessageAuthorizationEventData( /** Deterministic identity of the record, derived from the turn and span offsets so re-extracting the same span mints nothing new. */ + @CopilotExperimental @JsonProperty("recordId") String recordId, /** The human turn the quoted span was read from. */ + @CopilotExperimental @JsonProperty("turnIndex") Long turnIndex, /** Whether the claim granted or denied authority. */ + @CopilotExperimental @JsonProperty("polarity") PermissionMessageAuthorizationPolarity polarity, /** The kind of effect authorized, as an action-class identifier. */ + @CopilotExperimental @JsonProperty("actionClass") String actionClass, /** Start byte offset of the authorizing span within the turn. */ + @CopilotExperimental @JsonProperty("spanStart") Long spanStart, /** End byte offset of the authorizing span within the turn. */ + @CopilotExperimental @JsonProperty("spanEnd") Long spanEnd, /** Concrete named targets that appear verbatim inside the span. */ + @CopilotExperimental @JsonProperty("targetMembers") List targetMembers, /** The task the permission is scoped to, when the human named one. */ + @CopilotExperimental @JsonProperty("task") String task, /** The trusted version discriminator, when one exists. Exact shell-command grants carry the byte-identical commands grounded in the human span; world-derived classes carry a file object, remote tip, or runner only when that state was captured safely. An opaque object mirroring the runtime's adjacently-tagged resolution. */ + @CopilotExperimental @JsonProperty("world") Object world ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationReadEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationReadEvent.java index 7a372823c6..9415ee6d0d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationReadEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PermissionMessageAuthorizationReadEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "permission.messageAuthorizationRead". Historical decode-only extractor progress marker. Current runtimes do not run or resume extraction from it. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,12 +35,15 @@ public final class PermissionMessageAuthorizationReadEvent extends SessionEvent public void setData(PermissionMessageAuthorizationReadEventData data) { this.data = data; } /** Data payload for {@link PermissionMessageAuthorizationReadEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record PermissionMessageAuthorizationReadEventData( /** The human turn that was read by the proposer. */ + @CopilotExperimental @JsonProperty("turnIndex") Long turnIndex, /** Whether this read activates ongoing message-backed extraction. False for a contextual-assent-only pass while auto-approval is off, so unrelated future messages remain outside extraction. */ + @CopilotExperimental @JsonProperty("activatesExtraction") Boolean activatesExtraction ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/PromptCacheBreakEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/PromptCacheBreakEvent.java index 7ad39dec97..f3a21532ba 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/PromptCacheBreakEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/PromptCacheBreakEvent.java @@ -67,6 +67,8 @@ public record PromptCacheBreakEventData( @JsonProperty("toolsRemovedRaw") List toolsRemovedRaw, /** Raw names of tools redefined since the prior call, restricted because a tool name can be user-authored */ @JsonProperty("toolsRedefinedRaw") List toolsRedefinedRaw, + /** Changed definition parts of redefined tools, as `tool:part` entries; property-level parts only for telemetry-safe tools, whose other names are hashed */ + @JsonProperty("toolsRedefinedParts") List toolsRedefinedParts, /** Whether the tool list kept its members but changed their order */ @JsonProperty("toolsReordered") Boolean toolsReordered, /** Names of the system-prompt segments whose content changed */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaAccessState.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaAccessState.java new file mode 100644 index 0000000000..e9d9ef966e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaAccessState.java @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Access policy reported by the quota service. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaAccessState { + /** The {@code allowed} variant. */ + ALLOWED("allowed"), + /** The {@code denied} variant. */ + DENIED("denied"), + /** The {@code not_required} variant. */ + NOT_REQUIRED("not_required"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"); + + private final String value; + ProviderQuotaAccessState(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaAccessState fromValue(String value) { + for (ProviderQuotaAccessState v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaAccessState value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaAcquisitionStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaAcquisitionStatus.java new file mode 100644 index 0000000000..0b2c8aaa51 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaAcquisitionStatus.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Acquisition state, independent of the service's business access/capacity verdict. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaAcquisitionStatus { + /** The {@code succeeded} variant. */ + SUCCEEDED("succeeded"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"), + /** The {@code failed} variant. */ + FAILED("failed"); + + private final String value; + ProviderQuotaAcquisitionStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaAcquisitionStatus fromValue(String value) { + for (ProviderQuotaAcquisitionStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaAcquisitionStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaBudgetMetadata.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaBudgetMetadata.java new file mode 100644 index 0000000000..c32d10903d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaBudgetMetadata.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Authoritative budget measurements and policy metadata, independent of provider. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProviderQuotaBudgetMetadata( + /** Whether the budget has unlimited entitlement. */ + @JsonProperty("unlimited") Boolean unlimited, + /** Exact budget entitlement, retaining incumbent fractional-unit compatibility. */ + @JsonProperty("entitlement") Double entitlement, + /** Exact measured consumption in the budget's unit. */ + @JsonProperty("consumed") Double consumed, + /** Service-reported remaining percentage for authoritative budget presentation. */ + @JsonProperty("remainingPercentage") Double remainingPercentage, + /** Whether service policy allows continued usage after exhaustion. */ + @JsonProperty("usageAllowedWhenExhausted") Boolean usageAllowedWhenExhausted, + /** Usage beyond entitlement, in the budget's unit. */ + @JsonProperty("overage") Double overage, + /** Whether additional usage is allowed when the budget is exhausted. */ + @JsonProperty("overageAllowedWhenExhausted") Boolean overageAllowedWhenExhausted, + /** Reset instant in epoch milliseconds when this budget actually defines a window. */ + @JsonProperty("resetAtEpochMs") Double resetAtEpochMs, + /** Whether the reset instant is an estimate. */ + @JsonProperty("resetEstimated") Boolean resetEstimated, + /** Whether this budget uses token-based billing. */ + @JsonProperty("tokenBasedBilling") Boolean tokenBasedBilling, + /** Optional additional-usage budget cap. */ + @JsonProperty("overageLimit") Double overageLimit +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaCapacityState.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaCapacityState.java new file mode 100644 index 0000000000..590168d881 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaCapacityState.java @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Capacity is independent of whether a numeric balance was supplied. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaCapacityState { + /** The {@code available} variant. */ + AVAILABLE("available"), + /** The {@code exhausted} variant. */ + EXHAUSTED("exhausted"), + /** The {@code unlimited} variant. */ + UNLIMITED("unlimited"), + /** The {@code not_required} variant. */ + NOT_REQUIRED("not_required"), + /** The {@code not_applicable} variant. */ + NOT_APPLICABLE("not_applicable"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"); + + private final String value; + ProviderQuotaCapacityState(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaCapacityState fromValue(String value) { + for (ProviderQuotaCapacityState v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaCapacityState value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaObservationKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaObservationKind.java new file mode 100644 index 0000000000..adc6826ebe --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaObservationKind.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Timing and purpose of a provider quota observation. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaObservationKind { + /** The {@code account_snapshot} variant. */ + ACCOUNT_SNAPSHOT("account_snapshot"), + /** The {@code admission_state} variant. */ + ADMISSION_STATE("admission_state"); + + private final String value; + ProviderQuotaObservationKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaObservationKind fromValue(String value) { + for (ProviderQuotaObservationKind v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaObservationKind value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaQuantityKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaQuantityKind.java new file mode 100644 index 0000000000..44f2509f0c --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaQuantityKind.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Interpretation permitted for independently reported quantities. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaQuantityKind { + /** The {@code authoritative_budget} variant. */ + AUTHORITATIVE_BUDGET("authoritative_budget"), + /** The {@code advisory_balance} variant. */ + ADVISORY_BALANCE("advisory_balance"), + /** The {@code none} variant. */ + NONE("none"); + + private final String value; + ProviderQuotaQuantityKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaQuantityKind fromValue(String value) { + for (ProviderQuotaQuantityKind v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaQuantityKind value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaState.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaState.java new file mode 100644 index 0000000000..ddb7fe9aa9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaState.java @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * An account quota reading. Absence of a quantity is unknown, never zero. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProviderQuotaState( + /** Provider/account whose service reported this state. */ + @JsonProperty("provider") ModelProviderRef provider, + /** Service-owned quota identifier within this provider. */ + @JsonProperty("quotaId") String quotaId, + /** Unit of entitledQuantity and availableQuantity. */ + @JsonProperty("unit") ProviderQuotaUnit unit, + /** Whether the service permits access, independently of balance. */ + @JsonProperty("accessState") ProviderQuotaAccessState accessState, + /** Service capacity state; missing quantities do not imply exhaustion. */ + @JsonProperty("capacityState") ProviderQuotaCapacityState capacityState, + /** Whether this is a GET account reading or a pre-response admission observation. Observations are never merged across kinds. */ + @JsonProperty("observationKind") ProviderQuotaObservationKind observationKind, + /** Whether quantities are authoritative budget measurements, advisory balances, or absent. Only authoritative budgets support percentage presentation. */ + @JsonProperty("quantityKind") ProviderQuotaQuantityKind quantityKind, + /** Acquisition outcome. Unavailable/failed readings have no service verdict; access/capacity unavailable are compatibility placeholders only. */ + @JsonProperty("acquisitionStatus") ProviderQuotaAcquisitionStatus acquisitionStatus, + /** When the runtime observed this reading, not a charge timestamp or guarantee that consumption has settled. */ + @JsonProperty("observedAt") String observedAt, + /** HTTP status from acquisition, when available. */ + @JsonProperty("httpStatus") Long httpStatus, + /** Service error code or client acquisition category, separate from the business-state reason. */ + @JsonProperty("acquisitionError") String acquisitionError, + /** Optional metadata for an authoritative budget. Advisory balances and admission-only observations do not populate this. */ + @JsonProperty("budgetMetadata") ProviderQuotaBudgetMetadata budgetMetadata, + /** Key for the backwards-compatible snapshots projection, when the authoritative budget supports that contract. */ + @JsonProperty("compatibilityKey") String compatibilityKey, + /** Explicit service admission flag, when reported. */ + @JsonProperty("hasQuota") Boolean hasQuota, + /** Independently reported signed 64-bit entitlement. -1 is an unlimited sentinel, not a capacity-state rewrite; omission and null are preserved. */ + @JsonProperty("entitledQuantity") Long entitledQuantity, + /** Independently reported signed 64-bit available quantity. Zero does not override the service access/capacity verdict; omission and null are preserved. */ + @JsonProperty("availableQuantity") Long availableQuantity, + /** Service-reported explanation for the state. */ + @JsonProperty("reason") String reason, + /** Service-reported quota source. */ + @JsonProperty("source") String source, + /** Service name owning this reading. */ + @JsonProperty("service") String service +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaUnit.java b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaUnit.java new file mode 100644 index 0000000000..d8dc0048d7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ProviderQuotaUnit.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Units explicitly reported by a quota provider. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaUnit { + /** The {@code ai_credits} variant. */ + AI_CREDITS("ai_credits"), + /** The {@code requests} variant. */ + REQUESTS("requests"), + /** The {@code tokens} variant. */ + TOKENS("tokens"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"); + + private final String value; + ProviderQuotaUnit(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaUnit fromValue(String value) { + for (ProviderQuotaUnit v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaUnit value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/RecommendedAutoTier.java b/java/sdk/src/generated/java/com/github/copilot/generated/RecommendedAutoTier.java index acccaf5c8e..748395b847 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/RecommendedAutoTier.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/RecommendedAutoTier.java @@ -10,28 +10,36 @@ import javax.annotation.processing.Generated; /** - * Auto preferences that Copilot API can recommend. + * Enabled Auto preferences that Copilot API can recommend. * * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum RecommendedAutoTier { +public final class RecommendedAutoTier { /** The {@code efficiency} variant. */ - EFFICIENCY("efficiency"), + public static final RecommendedAutoTier EFFICIENCY = new RecommendedAutoTier("efficiency"); /** The {@code balance} variant. */ - BALANCE("balance"), + public static final RecommendedAutoTier BALANCE = new RecommendedAutoTier("balance"); /** The {@code intelligence} variant. */ - INTELLIGENCE("intelligence"); + public static final RecommendedAutoTier INTELLIGENCE = new RecommendedAutoTier("intelligence"); private final String value; - RecommendedAutoTier(String value) { this.value = value; } + private RecommendedAutoTier(String value) { this.value = value; } @com.fasterxml.jackson.annotation.JsonValue public String getValue() { return value; } @com.fasterxml.jackson.annotation.JsonCreator public static RecommendedAutoTier fromValue(String value) { + if (value == null) return null; + if (value.isEmpty() || value.codePoints().anyMatch(c -> Character.isWhitespace(c) || Character.isISOControl(c))) + throw new IllegalArgumentException("RecommendedAutoTier requires a routing identifier"); for (RecommendedAutoTier v : values()) { if (v.value.equals(value)) return v; } - throw new IllegalArgumentException("Unknown RecommendedAutoTier value: " + value); + return new RecommendedAutoTier(value); } + /** Returns the known routing preferences. @return known values */ + public static RecommendedAutoTier[] values() { return new RecommendedAutoTier[] { EFFICIENCY, BALANCE, INTELLIGENCE }; } + @Override public boolean equals(Object other) { return other instanceof RecommendedAutoTier v && value.equals(v.value); } + @Override public int hashCode() { return value.hashCode(); } + @Override public String toString() { return value; } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoModeResolvedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoModeResolvedEvent.java index 88b06f4471..f2d8a33293 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoModeResolvedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoModeResolvedEvent.java @@ -10,14 +10,18 @@ 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; /** * Session event "session.auto_mode_resolved". Auto Intent resolution: the concrete model the session settled on for the first prompt of an auto-mode session, and why. Lets SDK clients render the chosen model and the full reason it was picked. The core selection fields (chosenModel/reasoningBucket/categoryScores) are stable; the routing-analytics fields (predictedLabel/confidence/candidateModels) mirror the upstream intent service and may evolve, hence the event's experimental stability. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -33,6 +37,7 @@ public final class SessionAutoModeResolvedEvent extends SessionEvent { public void setData(SessionAutoModeResolvedEventData data) { this.data = data; } /** Data payload for {@link SessionAutoModeResolvedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionAutoModeResolvedEventData( @@ -56,6 +61,8 @@ public record SessionAutoModeResolvedEventData( @JsonProperty("fallback") Boolean fallback, /** Server-provided reason for falling back, when available */ @JsonProperty("fallbackReason") String fallbackReason, + /** Short human-readable sentence from the routing service explaining why this model was chosen, for display alongside the model. Present only when the service supplied one: it is omitted for on-device selections, when the service did not provide an explanation, and when a replayed decision made no routing call. The text is display-only and drawn from a fixed catalogue; several distinct routing categories share identical wording, so it cannot be used to recover the category or keyed on programmatically. */ + @JsonProperty("selectionReason") String selectionReason, /** Whether a sticky model choice overrode the router result */ @JsonProperty("stickyOverride") Boolean stickyOverride, /** Server-reported router processing time in milliseconds */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoTierRecommendationEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoTierRecommendationEvent.java index 9dd00a4586..5846359232 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoTierRecommendationEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionAutoTierRecommendationEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.auto_tier_recommendation". Live-only Auto preference recommendation from Copilot API after a successful Auto model call. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionAutoTierRecommendationEvent extends SessionEvent { public void setData(SessionAutoTierRecommendationEventData data) { this.data = data; } /** Data payload for {@link SessionAutoTierRecommendationEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionAutoTierRecommendationEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionBinaryAssetEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionBinaryAssetEvent.java index e925f2320a..d5e7a3f13f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionBinaryAssetEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionBinaryAssetEvent.java @@ -10,13 +10,17 @@ 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; /** * Session event "session.binary_asset". Canonical bytes for a content-addressed binary asset shared by reference across events + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -32,6 +36,7 @@ public final class SessionBinaryAssetEvent extends SessionEvent { public void setData(SessionBinaryAssetEventData data) { this.data = data; } /** Data payload for {@link SessionBinaryAssetEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionBinaryAssetEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasClosedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasClosedEvent.java index b660c0be66..b21bed7393 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasClosedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasClosedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.canvas.closed". Payload of `session.canvas.closed` with the closed canvas instance ID, provider ID, and canvas ID. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionCanvasClosedEvent extends SessionEvent { public void setData(SessionCanvasClosedEventData data) { this.data = data; } /** Data payload for {@link SessionCanvasClosedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionCanvasClosedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasOpenedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasOpenedEvent.java index 018e6a234d..fc371c2c59 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasOpenedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasOpenedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.canvas.opened". Payload of `session.canvas.opened` with canvas instance and provider IDs plus optional icon, title, status, URL, and input. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionCanvasOpenedEvent extends SessionEvent { public void setData(SessionCanvasOpenedEventData data) { this.data = data; } /** Data payload for {@link SessionCanvasOpenedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionCanvasOpenedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRecordedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRecordedEvent.java index 6f2fb42594..a2bcb80ce4 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRecordedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRecordedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.canvas.recorded". Durable record that a canvas instance is open, used to restore open canvases on cold session resume. Intentionally omits the transient url and availability. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionCanvasRecordedEvent extends SessionEvent { public void setData(SessionCanvasRecordedEventData data) { this.data = data; } /** Data payload for {@link SessionCanvasRecordedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionCanvasRecordedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRegistryChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRegistryChangedEvent.java index 0a6a9b62de..d97ef0ad95 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRegistryChangedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRegistryChangedEvent.java @@ -10,13 +10,17 @@ 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; /** * Session event "session.canvas.registry_changed". Payload of `session.canvas.registry_changed` listing the canvas declarations currently available. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -32,6 +36,7 @@ public final class SessionCanvasRegistryChangedEvent extends SessionEvent { public void setData(SessionCanvasRegistryChangedEventData data) { this.data = data; } /** Data payload for {@link SessionCanvasRegistryChangedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionCanvasRegistryChangedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRemovedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRemovedEvent.java index cea7ab09af..30181c1f13 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRemovedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasRemovedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.canvas.removed". Durable record that a canvas instance was closed, superseding a prior instance_recorded during resume replay. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionCanvasRemovedEvent extends SessionEvent { public void setData(SessionCanvasRemovedEventData data) { this.data = data; } /** Data payload for {@link SessionCanvasRemovedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionCanvasRemovedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasUnavailableEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasUnavailableEvent.java index 4e4397ecb6..5ff62e59b7 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasUnavailableEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCanvasUnavailableEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.canvas.unavailable". Transient signal that an open canvas instance's provider has dropped (for example the extension is reloading mid-session). The host should keep the panel mounted and surface a reconnecting affordance rather than tearing it down; a subsequent `session.canvas.opened` for the same instanceId clears the affordance once the provider reconnects with a fresh url. Ephemeral and never persisted, so it is never replayed on cold resume. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionCanvasUnavailableEvent extends SessionEvent { public void setData(SessionCanvasUnavailableEventData data) { this.data = data; } /** Data payload for {@link SessionCanvasUnavailableEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionCanvasUnavailableEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCompletionReceiptEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCompletionReceiptEvent.java index 15670c992a..ef6ad4eef1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionCompletionReceiptEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionCompletionReceiptEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.completion_receipt". Behavior-neutral record of structured runtime facts present when an agent completion decision is accepted. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionCompletionReceiptEvent extends SessionEvent { public void setData(SessionCompletionReceiptEventData data) { this.data = data; } /** Data payload for {@link SessionCompletionReceiptEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionCompletionReceiptEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionEvent.java index 33abf0411a..563b8c6e6b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionEvent.java @@ -54,6 +54,7 @@ @JsonSubTypes.Type(value = SessionSnapshotRewindEvent.class, name = "session.snapshot_rewind"), @JsonSubTypes.Type(value = SessionShutdownEvent.class, name = "session.shutdown"), @JsonSubTypes.Type(value = SessionUsageCheckpointEvent.class, name = "session.usage_checkpoint"), + @JsonSubTypes.Type(value = SessionQuotaObservationEvent.class, name = "session.quota_observation"), @JsonSubTypes.Type(value = SessionContextChangedEvent.class, name = "session.context_changed"), @JsonSubTypes.Type(value = SessionUsageInfoEvent.class, name = "session.usage_info"), @JsonSubTypes.Type(value = SessionContextClearedEvent.class, name = "session.context_cleared"), @@ -96,6 +97,7 @@ @JsonSubTypes.Type(value = ToolUserRequestedEvent.class, name = "tool.user_requested"), @JsonSubTypes.Type(value = ToolExecutionStartEvent.class, name = "tool.execution_start"), @JsonSubTypes.Type(value = ToolExecutionPartialResultEvent.class, name = "tool.execution_partial_result"), + @JsonSubTypes.Type(value = ToolShellOutputEvent.class, name = "tool.shell_output"), @JsonSubTypes.Type(value = ToolExecutionProgressEvent.class, name = "tool.execution_progress"), @JsonSubTypes.Type(value = ToolExecutionCompleteEvent.class, name = "tool.execution_complete"), @JsonSubTypes.Type(value = ToolSearchActivatedEvent.class, name = "tool_search.activated"), @@ -146,12 +148,14 @@ @JsonSubTypes.Type(value = SessionLimitsExhaustedRequestedEvent.class, name = "session_limits_exhausted.requested"), @JsonSubTypes.Type(value = SessionLimitsExhaustedCompletedEvent.class, name = "session_limits_exhausted.completed"), @JsonSubTypes.Type(value = SessionAutoModeResolvedEvent.class, name = "session.auto_mode_resolved"), + @JsonSubTypes.Type(value = SessionManagedPluginProgressEvent.class, name = "session.managed_plugin_progress"), @JsonSubTypes.Type(value = SessionManagedSettingsResolvedEvent.class, name = "session.managed_settings_resolved"), @JsonSubTypes.Type(value = SessionManagedSettingsEnforcedEvent.class, name = "session.managed_settings_enforced"), @JsonSubTypes.Type(value = CommandsChangedEvent.class, name = "commands.changed"), @JsonSubTypes.Type(value = CapabilitiesChangedEvent.class, name = "capabilities.changed"), @JsonSubTypes.Type(value = ExitPlanModeRequestedEvent.class, name = "exit_plan_mode.requested"), @JsonSubTypes.Type(value = ExitPlanModeCompletedEvent.class, name = "exit_plan_mode.completed"), + @JsonSubTypes.Type(value = HumanResponseRecordedEvent.class, name = "human_response.recorded"), @JsonSubTypes.Type(value = SessionToolsUpdatedEvent.class, name = "session.tools_updated"), @JsonSubTypes.Type(value = SessionBackgroundTasksChangedEvent.class, name = "session.background_tasks_changed"), @JsonSubTypes.Type(value = WorkflowRunUpdatedEvent.class, name = "workflow.run_updated"), @@ -207,6 +211,7 @@ public abstract sealed class SessionEvent permits SessionSnapshotRewindEvent, SessionShutdownEvent, SessionUsageCheckpointEvent, + SessionQuotaObservationEvent, SessionContextChangedEvent, SessionUsageInfoEvent, SessionContextClearedEvent, @@ -249,6 +254,7 @@ public abstract sealed class SessionEvent permits ToolUserRequestedEvent, ToolExecutionStartEvent, ToolExecutionPartialResultEvent, + ToolShellOutputEvent, ToolExecutionProgressEvent, ToolExecutionCompleteEvent, ToolSearchActivatedEvent, @@ -299,12 +305,14 @@ public abstract sealed class SessionEvent permits SessionLimitsExhaustedRequestedEvent, SessionLimitsExhaustedCompletedEvent, SessionAutoModeResolvedEvent, + SessionManagedPluginProgressEvent, SessionManagedSettingsResolvedEvent, SessionManagedSettingsEnforcedEvent, CommandsChangedEvent, CapabilitiesChangedEvent, ExitPlanModeRequestedEvent, ExitPlanModeCompletedEvent, + HumanResponseRecordedEvent, SessionToolsUpdatedEvent, SessionBackgroundTasksChangedEvent, WorkflowRunUpdatedEvent, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionCompletedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionCompletedEvent.java index 6742cdc3e3..3adc227a1c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionCompletedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionCompletedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.fusion_completed". Experimental durable aggregate outcome of a HydraFusion turn. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionFusionCompletedEvent extends SessionEvent { public void setData(SessionFusionCompletedEventData data) { this.data = data; } /** Data payload for {@link SessionFusionCompletedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionFusionCompletedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionResolvedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionResolvedEvent.java index 8bb0a5069b..20ef8aad9f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionResolvedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionResolvedEvent.java @@ -10,13 +10,17 @@ 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; /** * Session event "session.fusion_resolved". Experimental durable validated HydraFusion route and turn policy. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -32,6 +36,7 @@ public final class SessionFusionResolvedEvent extends SessionEvent { public void setData(SessionFusionResolvedEventData data) { this.data = data; } /** Data payload for {@link SessionFusionResolvedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionFusionResolvedEventData( @@ -64,18 +69,23 @@ public record SessionFusionResolvedEventData( /** Validated orchestration pattern selected for the turn. */ @JsonProperty("pattern") FusionPattern pattern, /** Short human-readable summary of the selected workflow, suitable for immediate client display after routing. May be absent in older durable events; omit the explanation or derive one from pattern and phasePlan. Display text, not a stable machine-readable value. */ + @CopilotExperimental @JsonProperty("hint") String hint, /** Presentation-neutral phase plan for clients that render workflow progress. */ + @CopilotExperimental @JsonProperty("phasePlan") List phasePlan, /** Concrete model selected for the primary solver phase. */ @JsonProperty("primaryModel") String primaryModel, /** Concrete model selected for Critique review, or the legacy Cascade judge/repair model when role-specific fields are absent. */ @JsonProperty("secondaryModel") String secondaryModel, /** Concrete model selected for Cascade escalation-gate calls, when required. */ + @CopilotExperimental @JsonProperty("judgeModel") String judgeModel, /** Concrete model selected for Cascade repair, when required. */ + @CopilotExperimental @JsonProperty("repairModel") String repairModel, /** Planned Critique phase identities, including independent critics with repeated model IDs. May be absent in older events; consumers then use secondaryModel for the legacy critic. */ + @CopilotExperimental @JsonProperty("critics") List critics, /** Concrete model used when the planned primary model cannot execute. */ @JsonProperty("fallbackModel") String fallbackModel, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionRouteFailedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionRouteFailedEvent.java index 25698b336b..eac6892f56 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionRouteFailedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionRouteFailedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.fusion_route_failed". Experimental durable HydraFusion routing failure and the deterministic concrete fallback selected for the turn. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionFusionRouteFailedEvent extends SessionEvent { public void setData(SessionFusionRouteFailedEventData data) { this.data = data; } /** Data payload for {@link SessionFusionRouteFailedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionFusionRouteFailedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionRouteStartedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionRouteStartedEvent.java index 139cb4f011..f8d350535e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionRouteStartedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionFusionRouteStartedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.fusion_route_started". Experimental transient signal that HydraFusion routing has started for an eligible turn. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SessionFusionRouteStartedEvent extends SessionEvent { public void setData(SessionFusionRouteStartedEventData data) { this.data = data; } /** Data payload for {@link SessionFusionRouteStartedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionFusionRouteStartedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedPluginProgressEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedPluginProgressEvent.java new file mode 100644 index 0000000000..f812f0fdcd --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedPluginProgressEvent.java @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Session event "session.managed_plugin_progress". Experimental transient presentation-neutral progress for organization-required plugin preparation. Clients should localize the phase copy and display plugin specs without translating them. + * + * @apiNote This event type is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionManagedPluginProgressEvent extends SessionEvent { + + @Override + public String getType() { return "session.managed_plugin_progress"; } + + @JsonProperty("data") + private SessionManagedPluginProgressEventData data; + + public SessionManagedPluginProgressEventData getData() { return data; } + public void setData(SessionManagedPluginProgressEventData data) { this.data = data; } + + /** Data payload for {@link SessionManagedPluginProgressEvent}. */ + @CopilotExperimental + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SessionManagedPluginProgressEventData( + /** Current preparation phase. */ + @JsonProperty("phase") ManagedPluginProgressPhase phase, + /** Ordered plugin install specs affected by this phase. Empty while managed settings initialize and when preparation completes. */ + @JsonProperty("pluginSpecs") List pluginSpecs + ) { + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsEnforcedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsEnforcedEvent.java index c712a220a9..2017f32e84 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsEnforcedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsEnforcedEvent.java @@ -10,12 +10,16 @@ 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; /** - * Session event "session.managed_settings_enforced". Runtime enforcement of enterprise managed settings: fires when the session blocks or caps a runtime action because enterprise policy governs it, so SDK clients can explain *why* an action was governed. Unlike `session.managed_settings_resolved` (which reports *what* is managed), this reports a concrete governed action — e.g. a user or host tried to turn on a bypass-permissions escalation while policy disables it. Emitted live (not persisted to the session event log) on user/host-initiated attempts only, never for silent policy application. Marked experimental while the managed-settings surface stabilizes. + * Session event "session.managed_settings_enforced". Runtime enforcement of enterprise managed settings: fires when the session blocks or caps a runtime action because enterprise policy governs it, so SDK clients can explain *why* an action was governed. Unlike `session.managed_settings_resolved` (which reports *what* is managed), this reports a concrete governed action — e.g. a user or host tried to turn on bypass permissions or Assisted Permissions while the corresponding policy disables it. Emitted live (not persisted to the session event log) on user/host-initiated attempts only, never for silent policy application. Marked experimental while the managed-settings surface stabilizes. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,14 +35,15 @@ public final class SessionManagedSettingsEnforcedEvent extends SessionEvent { public void setData(SessionManagedSettingsEnforcedEventData data) { this.data = data; } /** Data payload for {@link SessionManagedSettingsEnforcedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionManagedSettingsEnforcedEventData( /** The category of runtime action that managed policy governed. */ @JsonProperty("action") ManagedSettingsEnforcedAction action, - /** For a `bypass_permissions_blocked` action, which permission-escalation primitive was refused. Absent for actions without a specific escalation primitive. */ + /** For a `bypass_permissions_blocked` action, which permission-mode or escalation primitive was refused. Absent for actions without a specific primitive. */ @JsonProperty("escalation") ManagedSettingsEnforcedEscalation escalation, - /** The managed setting key responsible for the enforcement (e.g. `permissions.disableBypassPermissionsMode`). */ + /** The managed setting key responsible for the enforcement (for example `permissions.disableBypassPermissionsMode` or `permissions.disableAssistedPermissionsMode`). */ @JsonProperty("setting") String setting, /** Whether the enforcement was forced by fail-closed handling (managed policy could not be determined) rather than an explicit managed setting. When true, `setting` still names the restriction that was applied. */ @JsonProperty("failClosed") Boolean failClosed, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java index d495ec8cf6..27f062596c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionManagedSettingsResolvedEvent.java @@ -10,13 +10,17 @@ 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; /** * Session event "session.managed_settings_resolved". Effective enterprise managed settings applied to the session and their contributing channels. Emitted whenever managed policy is applied or reapplied, including session start, resume, and account switch. This ephemeral live snapshot is delivered to subscribers but not persisted to the session event log; initial resolution occurs before session.start. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -32,6 +36,7 @@ public final class SessionManagedSettingsResolvedEvent extends SessionEvent { public void setData(SessionManagedSettingsResolvedEventData data) { this.data = data; } /** Data payload for {@link SessionManagedSettingsResolvedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionManagedSettingsResolvedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java index 4ea1dbd46d..c6de1f0246 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionModelChangeEvent.java @@ -38,10 +38,18 @@ public record SessionModelChangeEventData( @JsonProperty("previousModel") String previousModel, /** Newly selected model identifier */ @JsonProperty("newModel") String newModel, + /** Provider selected for newModel. Omitted for legacy or unattributed selections; never inferred from a later selection. */ + @JsonProperty("providerId") String providerId, + /** Provider of previousModel, when known. A provider-only change is a model selection change even when the model identifiers are equal. */ + @JsonProperty("previousProviderId") String previousProviderId, /** Reasoning effort level before the model change, if applicable */ @JsonProperty("previousReasoningEffort") String previousReasoningEffort, /** Reasoning effort level after the model change, if applicable */ @JsonProperty("reasoningEffort") String reasoningEffort, + /** Model that owns effort embedded in an authored model selection. Omitted for independent reasoning-effort overrides and legacy events. */ + @JsonProperty("reasoningEffortModel") String reasoningEffortModel, + /** True when the reasoning effort is a managed-policy default bound to reasoningEffortModel. Omitted for agent-authored, user-authored, independent, and legacy effort. */ + @JsonProperty("reasoningEffortManaged") Boolean reasoningEffortManaged, /** Reasoning summary mode before the model change, if applicable */ @JsonProperty("previousReasoningSummary") ReasoningSummary previousReasoningSummary, /** Reasoning summary mode after the model change, if applicable */ @@ -52,6 +60,8 @@ public record SessionModelChangeEventData( @JsonProperty("verbosity") Verbosity verbosity, /** Context tier after the model change; null explicitly clears a previously selected tier */ @JsonProperty("contextTier") ContextTier contextTier, + /** True when contextTier is a managed-policy default. Omitted for user-authored and legacy values. */ + @JsonProperty("contextTierManaged") Boolean contextTierManaged, /** Reason the change happened, when not user-initiated. `"rate_limit_auto_switch"` for changes triggered by the auto-mode-switch rate-limit recovery path, or `"refusal_fallback"` when the active model declined a request (content refusal) and the runtime switched to the configured refusal-fallback model. UI clients can use this to render contextual copy. */ @JsonProperty("cause") String cause, /** Origin of the effective model change, when known. */ @@ -59,7 +69,31 @@ public record SessionModelChangeEventData( /** Previously committed Auto preference, when one was explicitly selected. */ @JsonProperty("previousAutoTier") AutoTier previousAutoTier, /** Committed Auto preference after the model configuration change, when applicable. */ - @JsonProperty("autoTier") AutoTier autoTier + @JsonProperty("autoTier") AutoTier autoTier, + /** True when autoTier is a managed-policy default. Omitted for user-authored and legacy values. */ + @JsonProperty("autoTierManaged") Boolean autoTierManaged ) { + + /** + * Creates event data with the components it had before later optional fields were added. + */ + public SessionModelChangeEventData( + String previousModel, + String newModel, + String previousReasoningEffort, + String reasoningEffort, + String reasoningEffortModel, + ReasoningSummary previousReasoningSummary, + ReasoningSummary reasoningSummary, + Verbosity previousVerbosity, + Verbosity verbosity, + ContextTier contextTier, + String cause, + ModelChangeSource source, + AutoTier previousAutoTier, + AutoTier autoTier + ) { + this(previousModel, newModel, null, null, previousReasoningEffort, reasoningEffort, reasoningEffortModel, null, previousReasoningSummary, reasoningSummary, previousVerbosity, verbosity, contextTier, null, cause, source, previousAutoTier, autoTier, null); + } } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionsChangedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionsChangedEvent.java index ef332617ac..818efd8527 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionsChangedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionPermissionsChangedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "session.permissions_changed". Permission-mode transition details. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,14 +35,18 @@ public final class SessionPermissionsChangedEvent extends SessionEvent { public void setData(SessionPermissionsChangedEventData data) { this.data = data; } /** Data payload for {@link SessionPermissionsChangedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionPermissionsChangedEventData( /** Permission mode before the change */ + @CopilotExperimental @JsonProperty("previousMode") PermissionMode previousMode, /** Permission mode after the change */ + @CopilotExperimental @JsonProperty("mode") PermissionMode mode, /** Explicit LLM judge model override used by assisted mode; omitted when the provider default applies */ + @CopilotExperimental @JsonProperty("assistedApprovalModel") String assistedApprovalModel ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionQuotaObservationEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionQuotaObservationEvent.java new file mode 100644 index 0000000000..c7b26660ca --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionQuotaObservationEvent.java @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Session event "session.quota_observation". A provider-owned quota observation, distinct from per-call usage and charge accounting. + * + * @apiNote This event type is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionQuotaObservationEvent extends SessionEvent { + + @Override + public String getType() { return "session.quota_observation"; } + + @JsonProperty("data") + private SessionQuotaObservationEventData data; + + public SessionQuotaObservationEventData getData() { return data; } + public void setData(SessionQuotaObservationEventData data) { this.data = data; } + + /** Data payload for {@link SessionQuotaObservationEvent}. */ + @CopilotExperimental + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SessionQuotaObservationEventData( + /** The admitted provider's state observation. Admission observations never contain quantities or reset/percentage semantics. */ + @JsonProperty("observation") ProviderQuotaState observation + ) { + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java index de54f8a3ce..1020ae8195 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionResumeEvent.java @@ -43,16 +43,26 @@ public record SessionResumeEventData( @JsonProperty("eventsFileSizeBytes") Long eventsFileSizeBytes, /** Model currently selected at resume time */ @JsonProperty("selectedModel") String selectedModel, + /** Provider of selectedModel at resume time, when explicitly selected. */ + @JsonProperty("providerId") String providerId, /** Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") */ @JsonProperty("reasoningEffort") String reasoningEffort, + /** Model that owns effort embedded in an authored model selection. Omitted for independent reasoning-effort overrides and legacy events. */ + @JsonProperty("reasoningEffortModel") String reasoningEffortModel, + /** True when the reasoning effort is a managed-policy default bound to reasoningEffortModel. Omitted for agent-authored, user-authored, independent, and legacy effort. */ + @JsonProperty("reasoningEffortManaged") Boolean reasoningEffortManaged, /** Reasoning summary mode used for model calls, if applicable (e.g. "none", "concise", "detailed") */ @JsonProperty("reasoningSummary") ReasoningSummary reasoningSummary, /** Output verbosity level used for model calls, if applicable (e.g. "low", "medium", "high") */ @JsonProperty("verbosity") Verbosity verbosity, /** Context tier currently selected at resume time; null when no tier is active */ @JsonProperty("contextTier") ContextTier contextTier, + /** True when contextTier is a managed-policy default. Omitted for user-authored and legacy values. */ + @JsonProperty("contextTierManaged") Boolean contextTierManaged, /** Auto routing preference active at resume time */ @JsonProperty("autoTier") AutoTier autoTier, + /** True when autoTier is a managed-policy default. Omitted for user-authored and legacy values. */ + @JsonProperty("autoTierManaged") Boolean autoTierManaged, /** Session limits currently configured at resume time; null when no limits are active */ @JsonProperty("sessionLimits") SessionLimitsConfig sessionLimits, /** Updated working directory and git context at resume time */ @@ -66,5 +76,29 @@ public record SessionResumeEventData( /** When true, tool calls and permission requests left in flight by the previous session lifetime remain pending after resume and the agentic loop awaits their results. User sends are queued behind the pending work until all such requests reach a terminal state. When false or omitted, pending work is normally marked as interrupted unless the resume passively joined live work owned by another client; sessionWasActive distinguishes that case. */ @JsonProperty("continuePendingWork") Boolean continuePendingWork ) { + + /** + * Creates event data with the components it had before later optional fields were added. + */ + public SessionResumeEventData( + OffsetDateTime resumeTime, + Long eventCount, + Long eventsFileSizeBytes, + String selectedModel, + String reasoningEffort, + String reasoningEffortModel, + ReasoningSummary reasoningSummary, + Verbosity verbosity, + ContextTier contextTier, + AutoTier autoTier, + SessionLimitsConfig sessionLimits, + WorkingDirectoryContext context, + Boolean alreadyInUse, + Boolean sessionWasActive, + Boolean remoteSteerable, + Boolean continuePendingWork + ) { + this(resumeTime, eventCount, eventsFileSizeBytes, selectedModel, null, reasoningEffort, reasoningEffortModel, null, reasoningSummary, verbosity, contextTier, null, autoTier, null, sessionLimits, context, alreadyInUse, sessionWasActive, remoteSteerable, continuePendingWork); + } } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java index cf8600e388..59f09bc9c2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionShutdownEvent.java @@ -10,6 +10,8 @@ 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; @@ -35,6 +37,12 @@ public final class SessionShutdownEvent extends SessionEvent { @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SessionShutdownEventData( + /** Complete accounting state captured atomically with receipt watermarks. */ + @JsonProperty("accountingSnapshot") UsageGetMetricsResult accountingSnapshot, + /** Provider/model accounting preserved for replay without consulting current provider selection. */ + @JsonProperty("providerModelMetrics") List providerModelMetrics, + /** Per-source accounting sequence watermarks subsuming preceding usage receipts. */ + @JsonProperty("usageAccountingWatermarks") Map usageAccountingWatermarks, /** Whether the session ended normally ("routine") or due to a crash/fatal error ("error") */ @JsonProperty("shutdownType") ShutdownType shutdownType, /** Error description when shutdownType is "error" */ @@ -42,6 +50,7 @@ public record SessionShutdownEventData( /** Total number of premium API requests used during the session */ @JsonProperty("totalPremiumRequests") Double totalPremiumRequests, /** Session-wide accumulated nano-AI units cost */ + @CopilotExperimental @JsonProperty("totalNanoAiu") Double totalNanoAiu, /** Session-wide per-token-type accumulated token counts */ @JsonProperty("tokenDetails") Map tokenDetails, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionStartEvent.java index b977ae036f..e3ea9d31e6 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionStartEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionStartEvent.java @@ -47,16 +47,26 @@ public record SessionStartEventData( @JsonProperty("startTime") OffsetDateTime startTime, /** Model selected at session creation time, if any */ @JsonProperty("selectedModel") String selectedModel, + /** Provider of selectedModel at creation time, when explicitly selected. */ + @JsonProperty("providerId") String providerId, /** Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") */ @JsonProperty("reasoningEffort") String reasoningEffort, + /** Model that owns effort embedded in an authored model selection. Omitted for independent reasoning-effort overrides and legacy events. */ + @JsonProperty("reasoningEffortModel") String reasoningEffortModel, + /** True when the reasoning effort is a managed-policy default bound to reasoningEffortModel. Omitted for agent-authored, user-authored, independent, and legacy effort. */ + @JsonProperty("reasoningEffortManaged") Boolean reasoningEffortManaged, /** Reasoning summary mode used for model calls, if applicable (e.g. "none", "concise", "detailed") */ @JsonProperty("reasoningSummary") ReasoningSummary reasoningSummary, /** Output verbosity level used for model calls, if applicable (e.g. "low", "medium", "high") */ @JsonProperty("verbosity") Verbosity verbosity, /** Context tier selected at session creation time for models with tiered context pricing; null when no tier is selected (e.g., non-tiered model) */ @JsonProperty("contextTier") ContextTier contextTier, + /** True when contextTier is a managed-policy default. Omitted for user-authored and legacy values. */ + @JsonProperty("contextTierManaged") Boolean contextTierManaged, /** Auto routing preference selected at session creation time */ @JsonProperty("autoTier") AutoTier autoTier, + /** True when autoTier is a managed-policy default. Omitted for user-authored and legacy values. */ + @JsonProperty("autoTierManaged") Boolean autoTierManaged, /** Session limits configured at session creation time, if any */ @JsonProperty("sessionLimits") SessionLimitsConfig sessionLimits, /** Working directory and git context at session start */ @@ -70,5 +80,31 @@ public record SessionStartEventData( /** When set, identifies a parent session whose context this session continues — e.g., a detached headless rem-agent run launched on the parent's interactive shutdown. Telemetry from this session is reported under the parent's session_id. */ @JsonProperty("detachedFromSpawningParentSessionId") String detachedFromSpawningParentSessionId ) { + + /** + * Creates event data with the components it had before later optional fields were added. + */ + public SessionStartEventData( + String sessionId, + Long version, + String producer, + String copilotVersion, + OffsetDateTime startTime, + String selectedModel, + String reasoningEffort, + String reasoningEffortModel, + ReasoningSummary reasoningSummary, + Verbosity verbosity, + ContextTier contextTier, + AutoTier autoTier, + SessionLimitsConfig sessionLimits, + WorkingDirectoryContext context, + GitHubMcpToolConfig gitHubMcpToolConfig, + Boolean alreadyInUse, + Boolean remoteSteerable, + String detachedFromSpawningParentSessionId + ) { + this(sessionId, version, producer, copilotVersion, startTime, selectedModel, null, reasoningEffort, reasoningEffortModel, null, reasoningSummary, verbosity, contextTier, null, autoTier, null, sessionLimits, context, gitHubMcpToolConfig, alreadyInUse, remoteSteerable, detachedFromSpawningParentSessionId); + } } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SessionUsageCheckpointEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SessionUsageCheckpointEvent.java index bdbc1add95..e0fd312afc 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SessionUsageCheckpointEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SessionUsageCheckpointEvent.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; +import java.util.Map; import javax.annotation.processing.Generated; /** @@ -37,6 +38,12 @@ public final class SessionUsageCheckpointEvent extends SessionEvent { public record SessionUsageCheckpointEventData( /** Session-wide accumulated nano-AI units cost at checkpoint time */ @JsonProperty("totalNanoAiu") Double totalNanoAiu, + /** Complete accounting state captured atomically with the receipt watermarks. */ + @JsonProperty("accountingSnapshot") UsageGetMetricsResult accountingSnapshot, + /** Provider/model accounting snapshot that subsumes preceding usage receipts. */ + @JsonProperty("providerModelMetrics") List providerModelMetrics, + /** Highest accounted sequence per source session; no per-call deduplication history is retained. */ + @JsonProperty("usageAccountingWatermarks") Map usageAccountingWatermarks, /** Total number of premium API requests used at checkpoint time */ @JsonProperty("totalPremiumRequests") Double totalPremiumRequests, /** Internal per-model prompt-cache state used to restore expiration tracking on resume */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SkillContextDeliveredEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SkillContextDeliveredEvent.java index a1f218d26e..c40c6dd6e3 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SkillContextDeliveredEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SkillContextDeliveredEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "skill.context_delivered". Exact skill context delivered to the model during a tool phase. This is not a user submission or another skill invocation. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SkillContextDeliveredEvent extends SessionEvent { public void setData(SkillContextDeliveredEventData data) { this.data = data; } /** Data payload for {@link SkillContextDeliveredEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SkillContextDeliveredEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SkillContextDeliveredRefEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SkillContextDeliveredRefEvent.java index b0468b3be0..a6eec3e548 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SkillContextDeliveredRefEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SkillContextDeliveredRefEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "skill.context_delivered_ref". Internal durable receipt that reconstructs exact model-visible skill context from earlier session content. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class SkillContextDeliveredRefEvent extends SessionEvent { public void setData(SkillContextDeliveredRefEventData data) { this.data = data; } /** Data payload for {@link SkillContextDeliveredRefEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SkillContextDeliveredRefEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/SkillInvokedRefEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/SkillInvokedRefEvent.java index 5dc09939e9..c05ab65c3d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/SkillInvokedRefEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/SkillInvokedRefEvent.java @@ -10,13 +10,17 @@ 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; /** * Session event "skill.invoked_ref". Internal durable skill invocation receipt whose content resolves from an earlier inline skill event in the same session. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -32,6 +36,7 @@ public final class SkillInvokedRefEvent extends SessionEvent { public void setData(SkillInvokedRefEventData data) { this.data = data; } /** Data payload for {@link SkillInvokedRefEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record SkillInvokedRefEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteEvent.java index 2b299df85b..1f2d180587 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteEvent.java @@ -10,6 +10,8 @@ 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; @@ -42,6 +44,7 @@ public record ToolExecutionCompleteEventData( /** Model identifier that generated this tool call */ @JsonProperty("model") String model, /** FIDES IFC label projected from tool ingress metadata (MCP `CallToolResult._meta` or synthesized built-in ingress labels). Persisted as `{ ifc: ... }` so the label survives session resume, including model-visible failure results. Experimental. */ + @CopilotExperimental @JsonProperty("mcpMeta") Object mcpMeta, /** CAPI interaction ID for correlating this tool execution with upstream telemetry */ @JsonProperty("interactionId") String interactionId, @@ -62,10 +65,15 @@ public record ToolExecutionCompleteEventData( /** Whether this tool execution ran inside a sandbox container */ @JsonProperty("sandboxed") Boolean sandboxed, /** Experimental shell completion facts captured before the persisted result contents are stripped. */ + @CopilotExperimental @JsonProperty("shellExecution") ToolExecutionCompleteShellExecution shellExecution, + /** Experimental. File mutations actually committed by a built-in file editing tool, in execution order. Present on successful edits and on partial failures when earlier mutations were committed. Paths are absolute in the session filesystem namespace. */ + @CopilotExperimental + @JsonProperty("fileEdits") List fileEdits, /** Tool call ID of the parent tool invocation when this event originates from a sub-agent */ @JsonProperty("parentToolCallId") String parentToolCallId, /** Experimental HydraFusion attribution for this tool completion. */ + @CopilotExperimental @JsonProperty("fusion") FusionAttribution fusion ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteFileEdit.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteFileEdit.java new file mode 100644 index 0000000000..02a6db87d3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteFileEdit.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * A file mutation that was actually committed by a built-in file editing tool. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ToolExecutionCompleteFileEdit( + /** Absolute path in the session filesystem namespace. */ + @JsonProperty("path") String path, + /** Kind of mutation committed at this path. */ + @JsonProperty("kind") ToolExecutionCompleteFileEditKind kind +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteFileEditKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteFileEditKind.java new file mode 100644 index 0000000000..cdf2f3aa50 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionCompleteFileEditKind.java @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Kind of file mutation committed by a built-in editing tool. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ToolExecutionCompleteFileEditKind { + /** The {@code create} variant. */ + public static final ToolExecutionCompleteFileEditKind CREATE = new ToolExecutionCompleteFileEditKind("create"); + /** The {@code edit} variant. */ + public static final ToolExecutionCompleteFileEditKind EDIT = new ToolExecutionCompleteFileEditKind("edit"); + /** The {@code delete} variant. */ + public static final ToolExecutionCompleteFileEditKind DELETE = new ToolExecutionCompleteFileEditKind("delete"); + /** An explicit unknown file operation kind. */ + public static final ToolExecutionCompleteFileEditKind UNKNOWN = new ToolExecutionCompleteFileEditKind("unknown"); + + private final String value; + private ToolExecutionCompleteFileEditKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ToolExecutionCompleteFileEditKind fromValue(String value) { + if (value == null) throw new IllegalArgumentException("Missing ToolExecutionCompleteFileEditKind value"); + if ("create".equals(value)) return CREATE; + if ("edit".equals(value)) return EDIT; + if ("delete".equals(value)) return DELETE; + if ("unknown".equals(value)) return UNKNOWN; + return new ToolExecutionCompleteFileEditKind(value); + } + @Override + public boolean equals(Object other) { + return other instanceof ToolExecutionCompleteFileEditKind kind && value.equals(kind.value); + } + @Override + public int hashCode() { return value.hashCode(); } + @Override + public String toString() { return value; } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionPartialResultEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionPartialResultEvent.java index e78d4d2a78..d8068f687a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionPartialResultEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionPartialResultEvent.java @@ -13,9 +13,10 @@ import javax.annotation.processing.Generated; /** - * Session event "tool.execution_partial_result". Streaming tool execution output for incremental result display + * Session event "tool.execution_partial_result". Deprecated merged replacement snapshot of shell output. Use tool.shell_output for append-only, stream-tagged output instead. * @since 1.0.0 */ +@Deprecated @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,12 +32,13 @@ public final class ToolExecutionPartialResultEvent extends SessionEvent { public void setData(ToolExecutionPartialResultEventData data) { this.data = data; } /** Data payload for {@link ToolExecutionPartialResultEvent}. */ + @Deprecated @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record ToolExecutionPartialResultEventData( /** Tool call ID this partial result belongs to */ @JsonProperty("toolCallId") String toolCallId, - /** Incremental output chunk from the running tool */ + /** Merged replacement snapshot from the running shell, not an append-only chunk */ @JsonProperty("partialOutput") String partialOutput ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionProgressEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionProgressEvent.java index 51be395194..2857bead45 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionProgressEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionProgressEvent.java @@ -10,6 +10,7 @@ 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; /** @@ -37,7 +38,10 @@ public record ToolExecutionProgressEventData( /** Tool call ID this progress notification belongs to */ @JsonProperty("toolCallId") String toolCallId, /** Human-readable progress status message (e.g., from an MCP server) */ - @JsonProperty("progressMessage") String progressMessage + @JsonProperty("progressMessage") String progressMessage, + /** Client-only structured progress metadata. Not model-facing tool output. */ + @CopilotExperimental + @JsonProperty("structuredContent") Object structuredContent ) { } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartEvent.java index 8bd686beaf..2c0d67c3f2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ToolExecutionStartEvent.java @@ -10,6 +10,7 @@ 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; /** @@ -67,6 +68,7 @@ public record ToolExecutionStartEventData( /** Tool call ID of the parent tool invocation when this event originates from a sub-agent */ @JsonProperty("parentToolCallId") String parentToolCallId, /** Experimental HydraFusion attribution for this tool execution. */ + @CopilotExperimental @JsonProperty("fusion") FusionAttribution fusion ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ToolShellOutputEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolShellOutputEvent.java new file mode 100644 index 0000000000..39e496ddc7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ToolShellOutputEvent.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Session event "tool.shell_output". Live, append-only shell output. Not persisted or replayed to late subscribers. Text is decoded and redacted per chunk; chunks need not contain complete lines. + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ToolShellOutputEvent extends SessionEvent { + + @Override + public String getType() { return "tool.shell_output"; } + + @JsonProperty("data") + private ToolShellOutputEventData data; + + public ToolShellOutputEventData getData() { return data; } + public void setData(ToolShellOutputEventData data) { this.data = data; } + + /** Data payload for {@link ToolShellOutputEvent}. */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record ToolShellOutputEventData( + /** Tool call ID that owns this shell output */ + @JsonProperty("toolCallId") String toolCallId, + /** Output source. Omission means stdout. Terminal output has no separate stdout/stderr attribution. */ + @JsonProperty("stream") ToolShellOutputStream stream, + /** New output to append, without synthetic shell-result markers or stream-switch separators */ + @JsonProperty("text") String text, + /** Zero-based publication sequence across all output streams for this tool call. Not a byte offset or an OS write-order guarantee. */ + @JsonProperty("sequence") Long sequence + ) { + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/ToolShellOutputStream.java b/java/sdk/src/generated/java/com/github/copilot/generated/ToolShellOutputStream.java new file mode 100644 index 0000000000..6a424b0772 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/ToolShellOutputStream.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import javax.annotation.processing.Generated; + +/** + * Shell output source. Terminal output has no separate stdout/stderr attribution. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ToolShellOutputStream { + /** The {@code stdout} variant. */ + STDOUT("stdout"), + /** The {@code stderr} variant. */ + STDERR("stderr"), + /** The {@code terminal} variant. */ + TERMINAL("terminal"); + + private final String value; + ToolShellOutputStream(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ToolShellOutputStream fromValue(String value) { + for (ToolShellOutputStream v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ToolShellOutputStream value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UiEphemeralQueryEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/UiEphemeralQueryEvent.java index b2f390e823..2c5620b443 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/UiEphemeralQueryEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UiEphemeralQueryEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "ui.ephemeral_query". Ordered output and terminal state for a transient query that does not modify conversation history. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class UiEphemeralQueryEvent extends SessionEvent { public void setData(UiEphemeralQueryEventData data) { this.data = data; } /** Data payload for {@link UiEphemeralQueryEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record UiEphemeralQueryEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageAccountingIdentity.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageAccountingIdentity.java new file mode 100644 index 0000000000..317aefc28c --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageAccountingIdentity.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Ordered accounting identity assigned under the source session's emission lock. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageAccountingIdentity( + /** Session that assigned this accounting sequence. */ + @JsonProperty("sourceSessionId") String sourceSessionId, + /** Monotonically increasing sequence within the source session. */ + @JsonProperty("sequence") Long sequence, + /** Existing API call identifier, or a runtime-generated identity when none was supplied. */ + @JsonProperty("usageId") String usageId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageGetMetricsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageGetMetricsResult.java new file mode 100644 index 0000000000..9142a777e9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageGetMetricsResult.java @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Accumulated session usage metrics, including premium request cost, token counts, model breakdown, and code-change totals. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageGetMetricsResult( + /** Completeness of totalNanoAiu. Numeric totals remain the reported subtotal; zero is not a claim of free usage when unavailable. */ + @JsonProperty("aiCreditsStatus") AiCreditsStatus aiCreditsStatus, + /** Total user-initiated premium request cost across all models (may be fractional due to multipliers) */ + @JsonProperty("totalPremiumRequestCost") Double totalPremiumRequestCost, + /** Raw count of user-initiated API requests */ + @JsonProperty("totalUserRequests") Long totalUserRequests, + /** Session-wide accumulated nano-AI units cost */ + @JsonProperty("totalNanoAiu") Double totalNanoAiu, + /** Session-wide per-token-type accumulated token counts */ + @JsonProperty("tokenDetails") Map tokenDetails, + /** Total time spent in model API calls (milliseconds) */ + @JsonProperty("totalApiDurationMs") Long totalApiDurationMs, + /** ISO 8601 timestamp when the session started */ + @JsonProperty("sessionStartTime") OffsetDateTime sessionStartTime, + /** Aggregated code change metrics */ + @JsonProperty("codeChanges") UsageMetricsCodeChanges codeChanges, + /** Per-model token and request metrics, keyed by model identifier */ + @JsonProperty("modelMetrics") Map modelMetrics, + /** Provider-attributed model metrics, ordered by first observed provider and model. +Null attribution preserves historical usage whose provider or model was not recorded. */ + @JsonProperty("providerModelMetrics") List providerModelMetrics, + /** Per-agent usage metrics, keyed by agent instance identifier. The main conversation uses the stable key `main`. */ + @JsonProperty("agentMetrics") Map agentMetrics, + /** Currently active model identifier */ + @JsonProperty("currentModel") String currentModel, + /** Input tokens from the most recent main-agent API call */ + @JsonProperty("lastCallInputTokens") Long lastCallInputTokens, + /** Output tokens from the most recent main-agent API call */ + @JsonProperty("lastCallOutputTokens") Long lastCallOutputTokens +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsAgentMetric.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsAgentMetric.java new file mode 100644 index 0000000000..a0e9f07d60 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsAgentMetric.java @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Usage attributed to one agent instance, including its identity, API duration, AI units, and per-model breakdown. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageMetricsAgentMetric( + /** Configured agent name, when this is a subagent */ + @JsonProperty("agentName") String agentName, + /** Human-readable label for this subagent invocation, copied from the originating `subagent.started` event. For task-tool subagents this is the invocation's task description rather than the agent's configured display name, so group by `agentName` for stable per-agent labels. */ + @JsonProperty("agentDisplayName") String agentDisplayName, + /** Time spent in model API calls by this agent, in milliseconds */ + @JsonProperty("totalApiDurationMs") Long totalApiDurationMs, + /** Accumulated nano-AI units cost for this agent */ + @JsonProperty("totalNanoAiu") Double totalNanoAiu, + /** Per-model usage for this agent, keyed by model identifier */ + @JsonProperty("modelMetrics") Map modelMetrics +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsCodeChanges.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsCodeChanges.java new file mode 100644 index 0000000000..92f117b26b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsCodeChanges.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Aggregated code change metrics + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageMetricsCodeChanges( + /** Total lines of code added */ + @JsonProperty("linesAdded") Long linesAdded, + /** Total lines of code removed */ + @JsonProperty("linesRemoved") Long linesRemoved, + /** Number of distinct files modified */ + @JsonProperty("filesModifiedCount") Long filesModifiedCount, + /** Distinct file paths modified during the session */ + @JsonProperty("filesModified") List filesModified +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetric.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetric.java new file mode 100644 index 0000000000..e55b06e3e9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetric.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.time.OffsetDateTime; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Per-model usage metrics, including request counts/costs, token usage, nano-AI units, and per-token-type details. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageMetricsModelMetric( + /** Completeness of this model's reported AI-credit subtotal. Explicitly reported zero is complete, not unavailable. */ + @JsonProperty("aiCreditsStatus") AiCreditsStatus aiCreditsStatus, + /** Request count and cost metrics for this model */ + @JsonProperty("requests") UsageMetricsModelMetricRequests requests, + /** Token usage metrics for this model */ + @JsonProperty("usage") UsageMetricsModelMetricUsage usage, + /** Latest known prompt-cache expiration for this model. A timestamp in the past indicates that the observed cache has expired. */ + @JsonProperty("cacheExpiresAt") OffsetDateTime cacheExpiresAt, + /** Accumulated nano-AI units cost for this model */ + @JsonProperty("totalNanoAiu") Double totalNanoAiu, + /** Token count details per type */ + @JsonProperty("tokenDetails") Map tokenDetails +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetricRequests.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetricRequests.java new file mode 100644 index 0000000000..56951bf8ee --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetricRequests.java @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Request count and cost metrics for this model + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageMetricsModelMetricRequests( + /** Number of API requests made with this model */ + @JsonProperty("count") Long count, + /** User-initiated premium request cost (with multiplier applied) */ + @JsonProperty("cost") Double cost +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetricTokenDetail.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetricTokenDetail.java new file mode 100644 index 0000000000..64bdc0c797 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetricTokenDetail.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Per-model token-detail entry containing the accumulated token count for one token type. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageMetricsModelMetricTokenDetail( + /** Accumulated token count for this token type */ + @JsonProperty("tokenCount") Long tokenCount +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetricUsage.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetricUsage.java new file mode 100644 index 0000000000..e984a1b823 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsModelMetricUsage.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Token usage metrics for this model + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageMetricsModelMetricUsage( + /** Total input tokens consumed */ + @JsonProperty("inputTokens") Long inputTokens, + /** Total output tokens produced */ + @JsonProperty("outputTokens") Long outputTokens, + /** Total tokens read from prompt cache */ + @JsonProperty("cacheReadTokens") Long cacheReadTokens, + /** Total tokens written to prompt cache */ + @JsonProperty("cacheWriteTokens") Long cacheWriteTokens, + /** Total output tokens used for reasoning */ + @JsonProperty("reasoningTokens") Long reasoningTokens +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsProviderModelMetric.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsProviderModelMetric.java new file mode 100644 index 0000000000..27e17e6ba6 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsProviderModelMetric.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Usage for one recorded provider and model, without merging identical model IDs across providers. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageMetricsProviderModelMetric( + /** Provider identity and product label captured when the call was dispatched; null when unknown. */ + @JsonProperty("provider") ModelProviderRef provider, + /** Model identity, or null for legacy aggregate-only usage. */ + @JsonProperty("modelId") String modelId, + /** Model display name captured at call time, when known. */ + @JsonProperty("modelDisplayName") String modelDisplayName, + /** Request, token, and cost totals for this provider/model. */ + @JsonProperty("metrics") UsageMetricsModelMetric metrics +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsTokenDetail.java b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsTokenDetail.java new file mode 100644 index 0000000000..2e30d0bf90 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/UsageMetricsTokenDetail.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Session-wide token-detail entry containing the accumulated token count for one token type. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageMetricsTokenDetail( + /** Accumulated token count for this token type */ + @JsonProperty("tokenCount") Long tokenCount +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunSettledEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunSettledEvent.java index 7cfef0df02..944dacd0df 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunSettledEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunSettledEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "workflow.run_settled". Ephemeral signal that a workflow run reached a terminal status. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class WorkflowRunSettledEvent extends SessionEvent { public void setData(WorkflowRunSettledEventData data) { this.data = data; } /** Data payload for {@link WorkflowRunSettledEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record WorkflowRunSettledEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunStartedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunStartedEvent.java index 656b51f596..fa12ead80a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunStartedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunStartedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "workflow.run_started". Ephemeral signal that a workflow run attempt began executing. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class WorkflowRunStartedEvent extends SessionEvent { public void setData(WorkflowRunStartedEventData data) { this.data = data; } /** Data payload for {@link WorkflowRunStartedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record WorkflowRunStartedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunUpdatedEvent.java b/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunUpdatedEvent.java index 6a51db49f6..1a9350e018 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunUpdatedEvent.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/WorkflowRunUpdatedEvent.java @@ -10,12 +10,16 @@ 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; /** * Session event "workflow.run_updated". Ephemeral invalidation signal for a changed workflow run. + * + * @apiNote This event type is experimental and may change in a future version. * @since 1.0.0 */ +@CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") @@ -31,6 +35,7 @@ public final class WorkflowRunUpdatedEvent extends SessionEvent { public void setData(WorkflowRunUpdatedEventData data) { this.data = data; } /** Data payload for {@link WorkflowRunUpdatedEvent}. */ + @CopilotExperimental @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public record WorkflowRunUpdatedEventData( diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountAuthInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountAuthInfo.java new file mode 100644 index 0000000000..b6a93fb206 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountAuthInfo.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; + +/** + * An interactive account whose model provider owns its credentials. It carries no GitHub credential. + * + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class AccountAuthInfo extends AuthInfo { + + @JsonProperty("type") + private final String type = "account"; + + @Override + public String getType() { return type; } + + /** Host coordinate owned by the account's model provider. */ + @JsonProperty("host") + private String host; + + /** Login identifying the provider-owned account. */ + @JsonProperty("login") + private String login; + + public String getHost() { return host; } + public void setHost(String host) { this.host = host; } + + public String getLogin() { return login; } + public void setLogin(String login) { this.login = login; } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountsAcquireEntraTokenParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountsAcquireEntraTokenParams.java deleted file mode 100644 index 34f3cfa8a3..0000000000 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AccountsAcquireEntraTokenParams.java +++ /dev/null @@ -1,41 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All 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; - -/** - * OneAuth token request supplied by a trusted host application. - * - * @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 AccountsAcquireEntraTokenParams( - /** Public client application id. */ - @JsonProperty("clientId") String clientId, - /** Tenant id or tenant selector, such as common or organizations. */ - @JsonProperty("tenantId") String tenantId, - /** Broker redirect URI registered for the client. Required: the OneAuth broker validates a non-empty, registered redirect URI for the public client (MSAL broker registration), so this is not a browser-flow vestige and cannot be omitted. */ - @JsonProperty("redirectUri") String redirectUri, - /** Exact delegated scopes to request. */ - @JsonProperty("scopes") List scopes, - /** Whether the broker may show interaction. */ - @JsonProperty("interaction") EntraTokenInteraction interaction, - /** Previously rejected token that OneAuth must bypass during renewal. */ - @JsonProperty("accessTokenToRenew") String accessTokenToRenew -) { -} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsCustomAgentInitialModelDecisionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsCustomAgentInitialModelDecisionParams.java new file mode 100644 index 0000000000..1555b36663 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsCustomAgentInitialModelDecisionParams.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 models a custom agent asks for, and the models actually available. + * + * @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 AgentsCustomAgentInitialModelDecisionParams( + /** The agent's declared `model:` entry, serialized. A single name or an ordered list of acceptable names. */ + @JsonProperty("agentModelsJson") String agentModelsJson, + /** The models available to this session, serialized in the shape the model list carries. */ + @JsonProperty("availableModelsJson") String availableModelsJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsCustomAgentInitialModelDecisionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsCustomAgentInitialModelDecisionResult.java new file mode 100644 index 0000000000..afd26e66af --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsCustomAgentInitialModelDecisionResult.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 model to switch to, and the warning to show when the agent's preference could not be met. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AgentsCustomAgentInitialModelDecisionResult( + /** The first available model that matches the agent's preferences. Absent when none of the requested models is available. */ + @JsonProperty("targetModel") String targetModel, + /** The reasoning effort attached to the selected model preference. Absent when that preference does not specify an effort. */ + @JsonProperty("reasoningEffort") String reasoningEffort, + /** 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. */ + @JsonProperty("warning") String warning +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetAvailableBuiltinsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetAvailableBuiltinsParams.java new file mode 100644 index 0000000000..fee8d13df9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetAvailableBuiltinsParams.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 feature flags to evaluate shipped agents 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 AgentsGetAvailableBuiltinsParams( + /** Feature flag values keyed by name, evaluated with the runtime's truthiness rules. Omit or pass null for no flags. */ + @JsonProperty("featureFlags") Map featureFlags, + /** Flag overrides keyed by name. A null entry uses the corresponding base flag; false explicitly disables it. Omit or pass null for no overrides. */ + @JsonProperty("overrides") Map overrides, + /** The surface asking, which gates agents that only apply to one client. Omit or pass null to apply no client filter. */ + @JsonProperty("context") String context +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenAcquireResultInteractionRequired.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetAvailableBuiltinsResult.java similarity index 70% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenAcquireResultInteractionRequired.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetAvailableBuiltinsResult.java index 96e635e027..14667e1269 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenAcquireResultInteractionRequired.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetAvailableBuiltinsResult.java @@ -10,21 +10,19 @@ 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; /** - * Variant {@code interaction-required} of {@link EntraTokenAcquireResult}. + * The shipped agents available under the requested flags. * * @since 1.0.0 */ -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonInclude(JsonInclude.Include.NON_NULL) @javax.annotation.processing.Generated("copilot-sdk-codegen") -public final class EntraTokenAcquireResultInteractionRequired extends EntraTokenAcquireResult { - - @JsonProperty("status") - private final String status = "interaction-required"; - - @Override - public String getStatus() { return status; } +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AgentsGetAvailableBuiltinsResult( + /** Available shipped agents, in the runtime's own order. */ + @JsonProperty("agents") List agents +) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinDefinitionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinDefinitionParams.java new file mode 100644 index 0000000000..cc69529078 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinDefinitionParams.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 shipped agent whose definition to load. + * + * @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 AgentsGetBuiltinDefinitionParams( + /** 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. */ + @JsonProperty("name") String name +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinDefinitionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinDefinitionResult.java new file mode 100644 index 0000000000..2fd4d55fa3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinDefinitionResult.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; + +/** + * One shipped agent's definition. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record 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. */ + @JsonProperty("definitionJson") String definitionJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinListingDefinitionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinListingDefinitionParams.java new file mode 100644 index 0000000000..9258f87d1b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinListingDefinitionParams.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 shipped agent whose listing entry to load. + * + * @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 AgentsGetBuiltinListingDefinitionParams( + /** 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. */ + @JsonProperty("name") String name +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinListingDefinitionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinListingDefinitionResult.java new file mode 100644 index 0000000000..f49d37d53b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinListingDefinitionResult.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; + +/** + * One shipped agent, projected for a listing. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record 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. */ + @JsonProperty("definitionJson") String definitionJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinsResult.java new file mode 100644 index 0000000000..d0148ac6d7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AgentsGetBuiltinsResult.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; + +/** + * The agents this runtime ships, named so a consumer can tell them apart from authored ones. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record AgentsGetBuiltinsResult( + /** Every agent name this runtime ships. */ + @JsonProperty("names") List names, + /** 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. */ + @JsonProperty("disableableNames") List disableableNames, + /** The subset of `names` defined by a shipped YAML definition. The remainder are special-cased in code and have no definition to load. */ + @JsonProperty("yamlBasedNames") List yamlBasedNames +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AiCreditsStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AiCreditsStatus.java new file mode 100644 index 0000000000..cc13ac67a4 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AiCreditsStatus.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; + +/** + * Whether the accumulated numeric AI-credit subtotal covers the observed calls. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum AiCreditsStatus { + /** The {@code complete} variant. */ + COMPLETE("complete"), + /** The {@code partial} variant. */ + PARTIAL("partial"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"); + + private final String value; + AiCreditsStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static AiCreditsStatus fromValue(String value) { + for (AiCreditsStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown AiCreditsStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfo.java index 5d82a47f85..7b03a6a29a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfo.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfo.java @@ -25,6 +25,7 @@ @JsonSubTypes.Type(value = TokenProviderAuthInfo.class, name = "token-provider"), @JsonSubTypes.Type(value = CopilotApiTokenAuthInfo.class, name = "copilot-api-token"), @JsonSubTypes.Type(value = UserAuthInfo.class, name = "user"), + @JsonSubTypes.Type(value = AccountAuthInfo.class, name = "account"), @JsonSubTypes.Type(value = GhCliAuthInfo.class, name = "gh-cli"), @JsonSubTypes.Type(value = ApiKeyAuthInfo.class, name = "api-key") }) diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfoType.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfoType.java index 5f81c8cf84..1be60446c1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfoType.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthInfoType.java @@ -22,6 +22,8 @@ public enum AuthInfoType { ENV("env"), /** The {@code user} variant. */ USER("user"), + /** The {@code account} variant. */ + ACCOUNT("account"), /** The {@code gh-cli} variant. */ GH_CLI("gh-cli"), /** The {@code api-key} variant. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginAccount.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginAccount.java new file mode 100644 index 0000000000..87405999ed --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginAccount.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; + +/** + * A credential-free account choice after sign-in. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record AuthLoginAccount( + /** Opaque identifier supplied to the next login step to select this account. */ + @JsonProperty("selectionId") String selectionId, + /** Host coordinate owned by the selected account's provider. */ + @JsonProperty("host") String host, + /** Human-readable login for the account choice. */ + @JsonProperty("login") String login, + /** Provider kind that owns this account choice. */ + @JsonProperty("kind") AccountKind kind +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginResultDto.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginResultDto.java index cbcffb6e54..ecbb106f12 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginResultDto.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginResultDto.java @@ -11,10 +11,11 @@ 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; /** - * Terminal result of an interactive login flow. + * Result of an interactive login flow. Pending consent or account selection is not terminal. * * @apiNote This type is experimental and may change in a future version. * @@ -25,11 +26,13 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record AuthLoginResultDto( - /** Terminal disposition of the login. */ + /** Current disposition of the login, including pending user decisions. */ @JsonProperty("status") AuthLoginResultStatus status, /** Host that was signed in, when completed. */ @JsonProperty("host") String host, /** Login that was signed in, when completed. */ - @JsonProperty("login") String login + @JsonProperty("login") String login, + /** Available accounts when sign-in is awaiting account selection, ordered with Microsoft 365 first. */ + @JsonProperty("accounts") List accounts ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginResultStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginResultStatus.java index 9e36ad0027..07becf2fb9 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginResultStatus.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginResultStatus.java @@ -11,7 +11,7 @@ import javax.annotation.processing.Generated; /** - * Terminal disposition of a login persistence attempt. + * Disposition of a login attempt, including pending user decisions. * * @apiNote This type is experimental and may change in a future version. * @@ -24,6 +24,8 @@ public enum AuthLoginResultStatus { COMPLETED("completed"), /** The {@code needs-plaintext-consent} variant. */ NEEDS_PLAINTEXT_CONSENT("needs-plaintext-consent"), + /** The {@code needs-account-selection} variant. */ + NEEDS_ACCOUNT_SELECTION("needs-account-selection"), /** The {@code declined} variant. */ DECLINED("declined"); diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginStepCompleted.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginStepCompleted.java index b1fb524643..a8ea53e123 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginStepCompleted.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthLoginStepCompleted.java @@ -32,7 +32,7 @@ public final class AuthLoginStepCompleted extends AuthLoginStep { @Override public String getKind() { return kind; } - /** The terminal login result. */ + /** Login result. When status is needs-plaintext-consent or needs-account-selection, advance with the user's decision to continue. */ @JsonProperty("result") private AuthLoginResultDto result; diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthReadValueActiveAccount.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthReadValueActiveAccount.java index 8ee2fb9416..e10579d0e6 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthReadValueActiveAccount.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AuthReadValueActiveAccount.java @@ -36,6 +36,13 @@ public final class AuthReadValueActiveAccount extends AuthReadValue { @JsonProperty("account") private AccountStatus account; + /** Credential-free identity metadata for the active account, including resolved Copilot user information when available. */ + @JsonProperty("authInfo") + private AuthIdentity authInfo; + public AccountStatus getAccount() { return account; } public void setAccount(AccountStatus account) { this.account = account; } + + public AuthIdentity getAuthInfo() { return authInfo; } + public void setAuthInfo(AuthIdentity authInfo) { this.authInfo = authInfo; } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTier.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTier.java index 00b18bcc94..cc568096a9 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTier.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTier.java @@ -10,30 +10,38 @@ import javax.annotation.processing.Generated; /** - * Routing preference used when the session model is `auto`. `fast` is an integrator-only latency preset and is not a first-party GitHub Copilot product preference. + * Extensible routing preference for the virtual `auto` model. New identifiers must be advertised and enabled by the provider. `fast` is an integrator-only latency preset. * * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum AutoTier { +public final class AutoTier { /** The {@code efficiency} variant. */ - EFFICIENCY("efficiency"), + public static final AutoTier EFFICIENCY = new AutoTier("efficiency"); /** The {@code balance} variant. */ - BALANCE("balance"), + public static final AutoTier BALANCE = new AutoTier("balance"); /** The {@code intelligence} variant. */ - INTELLIGENCE("intelligence"), + public static final AutoTier INTELLIGENCE = new AutoTier("intelligence"); /** The {@code fast} variant. */ - FAST("fast"); + public static final AutoTier FAST = new AutoTier("fast"); private final String value; - AutoTier(String value) { this.value = value; } + private AutoTier(String value) { this.value = value; } @com.fasterxml.jackson.annotation.JsonValue public String getValue() { return value; } @com.fasterxml.jackson.annotation.JsonCreator public static AutoTier fromValue(String value) { + if (value == null) return null; + if (value.isEmpty() || value.codePoints().anyMatch(c -> Character.isWhitespace(c) || Character.isISOControl(c))) + throw new IllegalArgumentException("AutoTier requires a routing identifier"); for (AutoTier v : values()) { if (v.value.equals(value)) return v; } - throw new IllegalArgumentException("Unknown AutoTier value: " + value); + return new AutoTier(value); } + /** Returns the known routing preferences. @return known values */ + public static AutoTier[] values() { return new AutoTier[] { EFFICIENCY, BALANCE, INTELLIGENCE, FAST }; } + @Override public boolean equals(Object other) { return other instanceof AutoTier v && value.equals(v.value); } + @Override public int hashCode() { return value.hashCode(); } + @Override public String toString() { return value; } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTierDescriptor.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTierDescriptor.java new file mode 100644 index 0000000000..49616a82a6 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTierDescriptor.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 javax.annotation.processing.Generated; + +/** + * A server-advertised routing preference. Identifiers and execution types are extensible. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record AutoTierDescriptor( + /** Opaque routing identifier transmitted unchanged to the provider. */ + @JsonProperty("id") String id, + /** Human-readable label, not a routing identifier. */ + @JsonProperty("displayName") String displayName, + /** Description displayed beside the preference. */ + @JsonProperty("description") String description, + /** Execution kind; this client supports `auto` preferences on the Auto model. */ + @JsonProperty("type") String type, + /** Current account-specific availability. */ + @JsonProperty("status") AutoTierStatus status +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTierMetadata.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTierMetadata.java new file mode 100644 index 0000000000..347bf55666 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTierMetadata.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; + +/** + * Account-bound discovery metadata for the virtual `auto` model. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record AutoTierMetadata( + /** Provider-default preference, used only when no explicit preference exists. */ + @JsonProperty("defaultTier") String defaultTier, + /** Routing preferences in the server's presentation order. */ + @JsonProperty("tiers") List tiers, + /** Provider that supplied this metadata, when the catalog is provider-attributed. */ + @JsonProperty("providerId") String providerId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTierStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTierStatus.java new file mode 100644 index 0000000000..8bf78312b6 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/AutoTierStatus.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; + +/** + * Availability of a server-advertised routing preference. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record AutoTierStatus( + /** Whether the provider permits selecting this preference. */ + @JsonProperty("enabled") Boolean enabled, + /** Extensible machine-readable unavailability reason. */ + @JsonProperty("reason") String reason, + /** Human-readable explanation of availability. */ + @JsonProperty("message") String message +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/BuiltinAgentSummary.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/BuiltinAgentSummary.java new file mode 100644 index 0000000000..65e75ff6f7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/BuiltinAgentSummary.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; + +/** + * A shipped agent, named and described. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record BuiltinAgentSummary( + /** The agent name, as it appears in `getBuiltins`. */ + @JsonProperty("name") String name, + /** One-line description of what the agent does. */ + @JsonProperty("description") String description +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsFinalizeInvocationEffectResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CommandsFinalizeInvocationEffectResult.java similarity index 89% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsFinalizeInvocationEffectResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/CommandsFinalizeInvocationEffectResult.java index 523a68564e..8718e93ebe 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsFinalizeInvocationEffectResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CommandsFinalizeInvocationEffectResult.java @@ -16,14 +16,15 @@ /** * Whether finalizing the invocation effect succeeded, and the failure reason when it did not. * - * @apiNote This method is experimental and may change in a future version. + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ @CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionCommandsFinalizeInvocationEffectResult( +record CommandsFinalizeInvocationEffectResult( /** Whether the pending invocation effect was finalized successfully. */ @JsonProperty("success") Boolean success, /** Failure reason when the invocation effect could not be finalized. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CommandsInvocationEffectOutcome.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CommandsInvocationEffectOutcome.java index 65293f1219..6a6bbbbb24 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CommandsInvocationEffectOutcome.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CommandsInvocationEffectOutcome.java @@ -19,7 +19,7 @@ */ @CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum CommandsInvocationEffectOutcome { +enum CommandsInvocationEffectOutcome { /** The {@code applied} variant. */ APPLIED("applied"), /** The {@code cancelled} variant. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectClientInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectClientInfo.java index e5b6b6f24d..5dd5e0a2b2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectClientInfo.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectClientInfo.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record ConnectClientInfo( +record ConnectClientInfo( /** Name of the host editor, e.g. `"vscode"`. */ @JsonProperty("editorName") String editorName, /** Version of the host editor, e.g. `"1.124.2"`. Ignored unless it looks like a version string. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java index 6500bb49ee..01fbacd610 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectParams.java @@ -24,7 +24,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record ConnectParams( +record ConnectParams( /** 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. */ @JsonProperty("enableGitHubTelemetryForwarding") Boolean enableGitHubTelemetryForwarding, /** Identity of the integrating host. Optional; omit it to keep the default attribution. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java index 41a200ff4b..01764bfe25 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectResult.java @@ -10,21 +10,18 @@ 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; /** * Handshake result reporting the server's protocol version and package version on success. * - * @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 ConnectResult( +record ConnectResult( /** Always true on success */ @JsonProperty("ok") Boolean ok, /** Server protocol version number */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryAccount.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryAccount.java new file mode 100644 index 0000000000..f41fd3dd79 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryAccount.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; + +/** + * Eligible account. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ConnectorDiscoveryAccount( + /** Opaque account ID. */ + @JsonProperty("accountId") String accountId, + /** Account metadata. */ + @JsonProperty("authInfo") ConnectorDiscoveryAuthInfo authInfo +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryAuthInfo.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryAuthInfo.java new file mode 100644 index 0000000000..3857e88ff1 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryAuthInfo.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; + +/** + * Account metadata. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ConnectorDiscoveryAuthInfo( + /** Authentication type. */ + @JsonProperty("type") AuthInfoType type, + /** Host. */ + @JsonProperty("host") String host, + /** Login. */ + @JsonProperty("login") String login +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenInteraction.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryAvailability.java similarity index 59% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenInteraction.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryAvailability.java index 4c83c4452e..00162fc078 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenInteraction.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryAvailability.java @@ -11,7 +11,7 @@ import javax.annotation.processing.Generated; /** - * How far OneAuth may go to acquire the requested token. + * Availability. * * @apiNote This type is experimental and may change in a future version. * @@ -19,23 +19,23 @@ */ @CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum EntraTokenInteraction { - /** The {@code silent} variant. */ - SILENT("silent"), - /** The {@code interactive} variant. */ - INTERACTIVE("interactive"), - /** The {@code force-interactive} variant. */ - FORCE_INTERACTIVE("force-interactive"); +public enum ConnectorDiscoveryAvailability { + /** The {@code enabled} variant. */ + ENABLED("enabled"), + /** The {@code disabled} variant. */ + DISABLED("disabled"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"); private final String value; - EntraTokenInteraction(String value) { this.value = value; } + ConnectorDiscoveryAvailability(String value) { this.value = value; } @com.fasterxml.jackson.annotation.JsonValue public String getValue() { return value; } @com.fasterxml.jackson.annotation.JsonCreator - public static EntraTokenInteraction fromValue(String value) { - for (EntraTokenInteraction v : values()) { + public static ConnectorDiscoveryAvailability fromValue(String value) { + for (ConnectorDiscoveryAvailability v : values()) { if (v.value.equals(value)) return v; } - throw new IllegalArgumentException("Unknown EntraTokenInteraction value: " + value); + throw new IllegalArgumentException("Unknown ConnectorDiscoveryAvailability value: " + value); } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryCatalogEntry.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryCatalogEntry.java new file mode 100644 index 0000000000..76c137c5e2 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorDiscoveryCatalogEntry.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Entry. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ConnectorDiscoveryCatalogEntry( + /** Name. */ + @JsonProperty("name") String name, + /** Display name. */ + @JsonProperty("displayName") String displayName, + /** Description. */ + @JsonProperty("description") String description, + /** Logo. */ + @JsonProperty("logo") String logo, + /** Tier. */ + @JsonProperty("tier") String tier, + /** Release tag. */ + @JsonProperty("releaseTag") String releaseTag, + /** Status. */ + @JsonProperty("status") ConnectorCatalogStatus status +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsGetAccountsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsGetAccountsResult.java new file mode 100644 index 0000000000..1334b673fa --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsGetAccountsResult.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; + +/** + * Eligible accounts. + * + * @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 ConnectorsGetAccountsResult( + /** Availability. */ + @JsonProperty("availability") ConnectorDiscoveryAvailability availability, + /** Eligible accounts. */ + @JsonProperty("accounts") List accounts +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsGetCapabilitiesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsGetCapabilitiesResult.java new file mode 100644 index 0000000000..dc1b256e27 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsGetCapabilitiesResult.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; + +/** + * Feature availability. + * + * @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 ConnectorsGetCapabilitiesResult( + /** API version. */ + @JsonProperty("apiVersion") Long apiVersion, + /** Availability. */ + @JsonProperty("availability") ConnectorDiscoveryAvailability availability, + /** Whether accounts are selected by opaque ID. */ + @JsonProperty("opaqueAccountSelection") Boolean opaqueAccountSelection, + /** Whether results are cached. */ + @JsonProperty("conditionalCache") Boolean conditionalCache +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsListParams.java similarity index 87% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsListParams.java index a111b7af4e..39a2c4a966 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HooksInvokeResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsListParams.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Optional output returned by an SDK callback hook. + * Selected account. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -23,7 +23,8 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HooksInvokeResult( - @JsonProperty("output") Object output +public record ConnectorsListParams( + /** Opaque account ID. */ + @JsonProperty("accountId") String accountId ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCustomizationsReloadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsListResult.java similarity index 68% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCustomizationsReloadResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsListResult.java index 5f6fa34306..ccba6073d4 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCustomizationsReloadResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsListResult.java @@ -15,7 +15,7 @@ import javax.annotation.processing.Generated; /** - * Diagnostics from reloading skill definitions, with warnings and errors as separate lists. + * Entries for the selected account. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -24,10 +24,14 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionCustomizationsReloadResult( - /** Warnings emitted while loading skills (e.g. skills that loaded but had issues) */ - @JsonProperty("warnings") List warnings, - /** Errors emitted while loading skills (e.g. skills that failed to load entirely) */ - @JsonProperty("errors") List errors +public record ConnectorsListResult( + /** Opaque account ID. */ + @JsonProperty("accountId") String accountId, + /** Revision. */ + @JsonProperty("revision") Long revision, + /** Refresh time in Unix epoch milliseconds. */ + @JsonProperty("refreshedAtMs") Long refreshedAtMs, + /** Entries. */ + @JsonProperty("connectors") List connectors ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostShutdownResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsRefreshParams.java similarity index 86% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostShutdownResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsRefreshParams.java index 46bb97b8d9..e5384ba3f2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostShutdownResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsRefreshParams.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Empty acknowledgement for a completed host lifecycle operation. + * Selected account. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -23,5 +23,8 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostShutdownResult() { +public record ConnectorsRefreshParams( + /** Opaque account ID. */ + @JsonProperty("accountId") String accountId +) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsRefreshResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsRefreshResult.java new file mode 100644 index 0000000000..a14c50fd28 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ConnectorsRefreshResult.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; + +/** + * Entries for the selected account. + * + * @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 ConnectorsRefreshResult( + /** Opaque account ID. */ + @JsonProperty("accountId") String accountId, + /** Revision. */ + @JsonProperty("revision") Long revision, + /** Refresh time in Unix epoch milliseconds. */ + @JsonProperty("refreshedAtMs") Long refreshedAtMs, + /** Entries. */ + @JsonProperty("connectors") List connectors +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CurrentModel.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CurrentModel.java index bf5f9a8b97..6fa6843944 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CurrentModel.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CurrentModel.java @@ -23,6 +23,12 @@ public record CurrentModel( /** Currently active model identifier */ @JsonProperty("modelId") String modelId, + /** Provider selected for this model. Together with modelId, identifies a catalog entry even when providers serve the same model. Omitted for unattributed legacy selections. */ + @JsonProperty("providerId") String providerId, + /** 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. */ + @JsonProperty("planBaseModelId") String planBaseModelId, + /** Provider of planBaseModelId, when that saved selection is provider-qualified. */ + @JsonProperty("planBaseProviderId") String planBaseProviderId, /** 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. */ @JsonProperty("reasoningEffort") String reasoningEffort, /** Context tier for models that support multiple context-window sizes. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationReloadOutcome.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationReloadOutcome.java new file mode 100644 index 0000000000..d8941b4567 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationReloadOutcome.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; + +/** + * Result of one customization reload component. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record CustomizationReloadOutcome( + /** Component whose reload was attempted or skipped */ + @JsonProperty("subsystem") CustomizationReloadSubsystem subsystem, + /** Whether the component reloaded, was skipped, or failed */ + @JsonProperty("status") CustomizationReloadStatus status, + /** Reason for a skipped component or description of a failure, when available */ + @JsonProperty("detail") String detail +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationReloadStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationReloadStatus.java new file mode 100644 index 0000000000..b34528549b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationReloadStatus.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.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Result of reloading a customization component. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class CustomizationReloadStatus { + /** The {@code reloaded} variant. */ + public static final CustomizationReloadStatus RELOADED = new CustomizationReloadStatus("reloaded"); + /** The {@code skipped} variant. */ + public static final CustomizationReloadStatus SKIPPED = new CustomizationReloadStatus("skipped"); + /** The {@code failed} variant. */ + public static final CustomizationReloadStatus FAILED = new CustomizationReloadStatus("failed"); + /** The default value when no recognized variant is available. */ + public static final CustomizationReloadStatus UNKNOWN = new CustomizationReloadStatus("unknown"); + + private final String value; + private CustomizationReloadStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static CustomizationReloadStatus fromValue(String value) { + if (RELOADED.value.equals(value)) return RELOADED; + if (SKIPPED.value.equals(value)) return SKIPPED; + if (FAILED.value.equals(value)) return FAILED; + if (UNKNOWN.value.equals(value)) return UNKNOWN; + if (value != null) return new CustomizationReloadStatus(value); + throw new IllegalArgumentException("Unknown CustomizationReloadStatus value: " + value); + } + @Override + public boolean equals(Object other) { + return other instanceof CustomizationReloadStatus that && value.equals(that.value); + } + @Override + public int hashCode() { return value.hashCode(); } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationReloadSubsystem.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationReloadSubsystem.java new file mode 100644 index 0000000000..989bde0a7c --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationReloadSubsystem.java @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 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; + +/** + * Component of session customization discovery. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class CustomizationReloadSubsystem { + /** The {@code repositoryContext} variant. */ + public static final CustomizationReloadSubsystem REPOSITORYCONTEXT = new CustomizationReloadSubsystem("repositoryContext"); + /** The {@code instructions} variant. */ + public static final CustomizationReloadSubsystem INSTRUCTIONS = new CustomizationReloadSubsystem("instructions"); + /** The {@code plugins} variant. */ + public static final CustomizationReloadSubsystem PLUGINS = new CustomizationReloadSubsystem("plugins"); + /** The {@code hooks} variant. */ + public static final CustomizationReloadSubsystem HOOKS = new CustomizationReloadSubsystem("hooks"); + /** The {@code skills} variant. */ + public static final CustomizationReloadSubsystem SKILLS = new CustomizationReloadSubsystem("skills"); + /** The {@code agents} variant. */ + public static final CustomizationReloadSubsystem AGENTS = new CustomizationReloadSubsystem("agents"); + /** The {@code mcp} variant. */ + public static final CustomizationReloadSubsystem MCP = new CustomizationReloadSubsystem("mcp"); + /** The {@code extensions} variant. */ + public static final CustomizationReloadSubsystem EXTENSIONS = new CustomizationReloadSubsystem("extensions"); + /** The default value when no recognized variant is available. */ + public static final CustomizationReloadSubsystem UNKNOWN = new CustomizationReloadSubsystem("unknown"); + + private final String value; + private CustomizationReloadSubsystem(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static CustomizationReloadSubsystem fromValue(String value) { + if (REPOSITORYCONTEXT.value.equals(value)) return REPOSITORYCONTEXT; + if (INSTRUCTIONS.value.equals(value)) return INSTRUCTIONS; + if (PLUGINS.value.equals(value)) return PLUGINS; + if (HOOKS.value.equals(value)) return HOOKS; + if (SKILLS.value.equals(value)) return SKILLS; + if (AGENTS.value.equals(value)) return AGENTS; + if (MCP.value.equals(value)) return MCP; + if (EXTENSIONS.value.equals(value)) return EXTENSIONS; + if (UNKNOWN.value.equals(value)) return UNKNOWN; + if (value != null) return new CustomizationReloadSubsystem(value); + throw new IllegalArgumentException("Unknown CustomizationReloadSubsystem value: " + value); + } + @Override + public boolean equals(Object other) { + return other instanceof CustomizationReloadSubsystem that && value.equals(that.value); + } + @Override + public int hashCode() { return value.hashCode(); } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationsReloadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationsReloadResult.java new file mode 100644 index 0000000000..2f84a8ee4b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/CustomizationsReloadResult.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; + +/** + * 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. + * + * @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 CustomizationsReloadResult( + /** Warnings from skill discovery */ + @JsonProperty("warnings") List warnings, + /** Errors from any component that could not be refreshed */ + @JsonProperty("errors") List errors, + /** Outcome of each component in reload order; a skipped component was not configured or loaded */ + @JsonProperty("outcomes") List outcomes +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredModel.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredModel.java new file mode 100644 index 0000000000..c019f85165 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/DiscoveredModel.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 com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.time.OffsetDateTime; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * 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. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record DiscoveredModel( + /** Provider-native model identifier. */ + @JsonProperty("id") String id, + /** Provider-reported display name. */ + @JsonProperty("name") String name, + /** Attribution for the adapter that produced this model row. */ + @JsonProperty("provenance") ModelProviderProvenance provenance, + /** Provider-reported artifact digest. */ + @JsonProperty("digest") String digest, + /** Provider-reported last-modified timestamp. */ + @JsonProperty("modifiedAt") OffsetDateTime modifiedAt, + /** Provider-reported artifact size in bytes. */ + @JsonProperty("sizeBytes") Long sizeBytes, + /** Provider-reported model artifact details. */ + @JsonProperty("details") ModelArtifactDetails details, + /** Provider-reported model capabilities. Omitted capability fields are unknown; explicit false values are preserved. */ + @JsonProperty("capabilities") ModelCapabilities capabilities, + /** Non-fatal warnings encountered while enriching this model. */ + @JsonProperty("warnings") List warnings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenAcquireResultOk.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenAcquireResultOk.java deleted file mode 100644 index 84e4d65970..0000000000 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EntraTokenAcquireResultOk.java +++ /dev/null @@ -1,51 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All 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; - -/** - * Variant {@code ok} of {@link EntraTokenAcquireResult}. - * - * @since 1.0.0 - */ -@JsonIgnoreProperties(ignoreUnknown = true) -@JsonInclude(JsonInclude.Include.NON_NULL) -@javax.annotation.processing.Generated("copilot-sdk-codegen") -public final class EntraTokenAcquireResultOk extends EntraTokenAcquireResult { - - @JsonProperty("status") - private final String status = "ok"; - - @Override - public String getStatus() { return status; } - - /** Opaque access token. */ - @JsonProperty("accessToken") - private String accessToken; - - /** Expiry as milliseconds since Unix epoch, when supplied by OneAuth. */ - @JsonProperty("expiresOnTimestamp") - private Double expiresOnTimestamp; - - /** Opaque OneAuth account id, when supplied by the broker. */ - @JsonProperty("accountId") - private String accountId; - - public String getAccessToken() { return accessToken; } - public void setAccessToken(String accessToken) { this.accessToken = accessToken; } - - public Double getExpiresOnTimestamp() { return expiresOnTimestamp; } - public void setExpiresOnTimestamp(Double expiresOnTimestamp) { this.expiresOnTimestamp = expiresOnTimestamp; } - - public String getAccountId() { return accountId; } - public void setAccountId(String accountId) { this.accountId = accountId; } -} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EnvironmentCapabilities.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EnvironmentCapabilities.java index f261c345db..3bbe3e31a0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EnvironmentCapabilities.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EnvironmentCapabilities.java @@ -10,14 +10,18 @@ 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; /** * Hosting capabilities and session capacity advertised by an environment. * + * @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) diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EnvironmentKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EnvironmentKind.java index d7ae216cce..f3d45cafc1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EnvironmentKind.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/EnvironmentKind.java @@ -7,13 +7,17 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import javax.annotation.processing.Generated; /** * GitHub Mission Control compute kind. * + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ +@CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") public enum EnvironmentKind { /** The {@code user-local} variant. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitCurrentBranchRemoteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitCurrentBranchRemoteParams.java new file mode 100644 index 0000000000..67f7b64880 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitCurrentBranchRemoteParams.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; + +/** + * Working-tree path a git query applies 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 GitCurrentBranchRemoteParams( + /** Absolute path to a directory inside the git working tree to query. */ + @JsonProperty("cwd") String cwd +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitCurrentBranchRemoteResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitCurrentBranchRemoteResult.java new file mode 100644 index 0000000000..faa7e52b3d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitCurrentBranchRemoteResult.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 remote the checked-out branch tracks. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record GitCurrentBranchRemoteResult( + /** 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. */ + @JsonProperty("remote") String remote +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubEnvironment.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubEnvironment.java index fffed71974..c17b2c9778 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubEnvironment.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubEnvironment.java @@ -10,14 +10,18 @@ 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; /** * Safe discovery information. Host-side relay bootstrap credentials are never included. * + * @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) diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnerOption.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnerOption.java new file mode 100644 index 0000000000..43e3d2d01d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnerOption.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; + +/** + * A GitHub login the authenticated user may act as: their own account, or an organization they belong to. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record GitHubOwnerOption( + /** The owner's GitHub login. */ + @JsonProperty("login") String login, + /** Which kind of owner this is. The authenticated user's own account is always reported as `user`. */ + @JsonProperty("type") String type +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersCancelParams.java similarity index 83% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersCancelParams.java index a22656359f..1ea9251095 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersCancelParams.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Result of configuring GitHub MCP. + * The owner listing to abandon. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -23,8 +23,8 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionMcpConfigureGitHubResult( - /** Whether GitHub MCP configuration changed. */ - @JsonProperty("changed") Boolean changed +record GitHubOwnersCancelParams( + /** Request id the listing was started with. */ + @JsonProperty("requestId") Long requestId ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersCancelResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersCancelResult.java new file mode 100644 index 0000000000..d70a718261 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersCancelResult.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Whether the id named a running owner listing. + * + * @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 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. */ + @JsonProperty("canceled") Boolean canceled +) { +} 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 new file mode 100644 index 0000000000..224b85f6dc --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersListParams.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; + +/** + * Credential to list owners under, and the request id that makes the listing cancellable. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record 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. */ + @JsonProperty("authInfo") Object authInfo +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersListResult.java new file mode 100644 index 0000000000..02f844bced --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersListResult.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 java.util.List; +import javax.annotation.processing.Generated; + +/** + * 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. + * + * @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 GitHubOwnersListResult( + /** The owners, on success: the authenticated user first, then the organizations they belong to. */ + @JsonProperty("owners") List owners, + /** 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. */ + @JsonProperty("message") String message, + /** A line the caller should log. Present only alongside `message`, and only for failures worth recording. */ + @JsonProperty("warning") String warning, + /** 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. */ + @JsonProperty("throwError") String throwError +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersRequestIdResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersRequestIdResult.java new file mode 100644 index 0000000000..8b5d180b05 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubOwnersRequestIdResult.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; + +/** + * 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. + * + * @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 GitHubOwnersRequestIdResult( + /** Request id to pass to `gitHubOwners.list` and, to abandon it, `gitHubOwners.cancel`. */ + @JsonProperty("requestId") Long requestId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubRepositoryAtPathParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubRepositoryAtPathParams.java new file mode 100644 index 0000000000..6a896bff33 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubRepositoryAtPathParams.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; + +/** + * Working-tree path whose owning GitHub repository should be resolved. + * + * @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 GitHubRepositoryAtPathParams( + /** Absolute path to a directory inside the git working tree to resolve. */ + @JsonProperty("path") String path +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubRepositoryAtPathResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubRepositoryAtPathResult.java new file mode 100644 index 0000000000..f32a3eafc0 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubRepositoryAtPathResult.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 GitHub repository that owns the requested path, when the selected remote (`origin`, else the first) is on a GitHub host. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record GitHubRepositoryAtPathResult( + /** Resolved repository identity, or null when the selected remote resolves to no GitHub host. */ + @JsonProperty("repository") GitHubRepositoryIdentity repository +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubRepositoryIdentity.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubRepositoryIdentity.java new file mode 100644 index 0000000000..a1d8c3acc3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitHubRepositoryIdentity.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; + +/** + * Owner, name, and host of a GitHub repository, as resolved from a git remote URL. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record GitHubRepositoryIdentity( + /** Repository owner login (user or organization). */ + @JsonProperty("owner") String owner, + /** Repository name, without the owner prefix or the `.git` suffix. */ + @JsonProperty("name") String name, + /** Host the remote points at, for example `github.com` or a GitHub Enterprise hostname. */ + @JsonProperty("host") String host +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitRemoteRepository.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitRemoteRepository.java new file mode 100644 index 0000000000..936e0f8159 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitRemoteRepository.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; + +/** + * A GitHub repository one of a working tree's remotes points at. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record GitRemoteRepository( + /** Account or organization owning the repository. */ + @JsonProperty("owner") String owner, + /** Repository name, without the owner. */ + @JsonProperty("name") String name, + /** GitHub host serving the repository, which is not `github.com` for a GitHub Enterprise remote. */ + @JsonProperty("host") String host, + /** Name of the first remote that produced this distinct repository entry, such as `origin` or `upstream`. */ + @JsonProperty("remoteName") String remoteName +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitReposFromRemotesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitReposFromRemotesParams.java new file mode 100644 index 0000000000..f9c351550f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitReposFromRemotesParams.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; + +/** + * Git working tree whose GitHub remotes should be listed. + * + * @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 GitReposFromRemotesParams( + /** Absolute path to the root of the git working tree. */ + @JsonProperty("gitRoot") String gitRoot +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitReposFromRemotesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitReposFromRemotesResult.java new file mode 100644 index 0000000000..cc62acf984 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitReposFromRemotesResult.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 GitHub repositories a working tree's remotes point at. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record GitReposFromRemotesResult( + /** One entry per distinct GitHub repository, in the order git reports the first remote for each repository. Empty when no remote points at a GitHub host, which a caller should read as `not connected to GitHub`. Failing to read the remotes is an error, not an empty list. */ + @JsonProperty("repositories") List repositories +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitWorkingDirectoryContextParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitWorkingDirectoryContextParams.java new file mode 100644 index 0000000000..17acdb64c3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GitWorkingDirectoryContextParams.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; + +/** + * Working-tree path a git query applies 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 GitWorkingDirectoryContextParams( + /** Absolute path to a directory inside the git working tree to query. */ + @JsonProperty("cwd") String cwd +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadForConfigDirParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadForConfigDirParams.java new file mode 100644 index 0000000000..a056698c92 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadForConfigDirParams.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 machine-wide state 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 GlobalStateLoadForConfigDirParams( + /** 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. */ + @JsonProperty("configDir") String configDir +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadResult.java new file mode 100644 index 0000000000..0b6dbb9d53 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateLoadResult.java @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All 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 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. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record GlobalStateLoadResult( + /** Plugins installed on this machine. */ + @JsonProperty("installedPlugins") List installedPlugins, + /** Models the user selected recently, most recent first. */ + @JsonProperty("recentModelIds") List recentModelIds, + /** When the host first ran on this machine. */ + @JsonProperty("firstLaunchAt") String firstLaunchAt, + /** Terminals the user has already been asked to set up, so the host does not ask twice. */ + @JsonProperty("askedSetupTerminals") List askedSetupTerminals, + /** Folders where the user declined the init prompt, so it stays hidden there. */ + @JsonProperty("suppressInitFolders") List suppressInitFolders, + /** Whether the sandbox onboarding has been shown. */ + @JsonProperty("sandboxOnboardingShown") Boolean sandboxOnboardingShown, + /** Whether the user declined to trust the sandbox credential proxy CA. */ + @JsonProperty("sandboxCredentialProxyCaDeclined") Boolean sandboxCredentialProxyCaDeclined, + /** Whether the app tip has been shown. */ + @JsonProperty("appTipShown") Boolean appTipShown, + /** Whether the one-off cleanup of stored reasoning summaries has run. */ + @JsonProperty("reasoningSummariesCleanupDone") Boolean reasoningSummariesCleanupDone, + /** Account used for the most recent sign-in. */ + @JsonProperty("lastLoggedInUser") LoggedInUser lastLoggedInUser, + /** Every account the host has signed in to on this machine. */ + @JsonProperty("loggedInUsers") List loggedInUsers, + /** Whether the user is a GitHub or Microsoft staff member, which unlocks internal-only behavior. */ + @JsonProperty("staff") Boolean staff, + /** Whether the user was recognized as GitHub staff. */ + @JsonProperty("staffGithub") Boolean staffGitHub, + /** Whether the user was recognized as Microsoft staff. */ + @JsonProperty("staffMicrosoft") Boolean staffMicrosoft, + /** When the staff-only model reset last ran. */ + @JsonProperty("staffModelResetAt") String staffModelResetAt, + /** When the staff-only log level migration last ran. */ + @JsonProperty("staffLogLevelMigrationAt") String staffLogLevelMigrationAt, + /** When the staff-only update channel migration last ran. */ + @JsonProperty("staffUpdateChannelMigrationAt") String staffUpdateChannelMigrationAt, + /** Folders the user has marked as trusted. */ + @JsonProperty("trustedFolders") List trustedFolders, + /** Whether the user has answered the prompt suggesting they install the desktop app. */ + @JsonProperty("appInstallNudgeResponded") Boolean appInstallNudgeResponded, + /** When the Auto-feedback hint was last shown, as an ISO 8601 timestamp. It enforces the once-per-day cap for non-staff users across restarts. */ + @JsonProperty("autoFeedbackLastPromptedAt") String autoFeedbackLastPromptedAt +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateWriteKeyParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateWriteKeyParams.java new file mode 100644 index 0000000000..f6ee53a57b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/GlobalStateWriteKeyParams.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 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. + * + * @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 GlobalStateWriteKeyParams( + /** 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. */ + @JsonProperty("configDir") String configDir, + /** 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. */ + @JsonProperty("key") String key, + /** Value to store for the key. Omit it, or pass null, to remove the key instead. */ + @JsonProperty("value") Object value +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java index 9ed02d28b0..102b13f428 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookInvokeRequest.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HookInvokeRequest( +record HookInvokeRequest( @JsonProperty("sessionId") String sessionId, @JsonProperty("hookType") HookType hookType, @JsonProperty("input") Object input diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookInvokeResponse.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookInvokeResponse.java new file mode 100644 index 0000000000..7a799afcca --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HookInvokeResponse.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All 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; + +/** + * Optional output returned by an SDK callback hook. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record HookInvokeResponse( + @JsonProperty("output") Object output +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetConfigurationResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostConfiguration.java similarity index 92% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetConfigurationResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostConfiguration.java index 5c865323b7..15187b8763 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetConfigurationResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostConfiguration.java @@ -16,14 +16,15 @@ /** * Normalized listener settings delivered only to the supervised hosting participant. * - * @apiNote This method is experimental and may change in a future version. + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ @CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostGetConfigurationResult( +record HostConfiguration( /** Normalized local listener settings, absent for relay-only hosts. */ @JsonProperty("localServer") HostLocalServerConfiguration localServer, /** Requested GitHub Mission Control registration. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostCreateSessionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostCreateSessionParams.java index 0a3fbb1f5f..348774f645 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostCreateSessionParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostCreateSessionParams.java @@ -24,11 +24,14 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostCreateSessionParams( +record HostCreateSessionParams( /** Unique identity for this participation, independent of the session lifetime. */ @JsonProperty("handoffId") String handoffId, /** Resume an app-owned durable session instead of creating a new session. */ @JsonProperty("resume") Boolean resume, + /** For an explicit publication, borrow an existing owning-connection resident +instead of invoking the resume factory. Cold sessions still use the factory. */ + @JsonProperty("preferResident") Boolean preferResident, /** Host-selected SDK creation or resume settings, without executable callbacks or tools. */ @JsonProperty("config") Map config ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostEmptyResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostEmptyResult.java index 658f68f3b2..8c15b12414 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostEmptyResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostEmptyResult.java @@ -10,13 +10,17 @@ 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; /** * Empty acknowledgement for a completed host lifecycle operation. * + * @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) diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetEnvironmentCredentialsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostEnvironmentCredentials.java similarity index 91% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetEnvironmentCredentialsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostEnvironmentCredentials.java index b6c5403858..607fc9db6c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetEnvironmentCredentialsResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostEnvironmentCredentials.java @@ -16,14 +16,15 @@ /** * Private credentials delivered only to a runtime-owned Mission Control hosting participant. * - * @apiNote This method is experimental and may change in a future version. + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ @CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostGetEnvironmentCredentialsResult( +record HostEnvironmentCredentials( /** Current bearer token for the authenticated GitHub identity. */ @JsonProperty("token") String token, /** Hostname of the authenticated GitHub service. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostExitReason.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostExitReason.java index 28a5391b27..929ed09af7 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostExitReason.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostExitReason.java @@ -7,8 +7,17 @@ package com.github.copilot.generated.rpc; +import com.github.copilot.CopilotExperimental; import javax.annotation.processing.Generated; +/** + * Values for {@code HostExitReason}. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") public enum HostExitReason { /** The {@code disposed} variant. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostExitedNotification.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostExitedNotification.java index aa930eefe1..6c39e744c8 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostExitedNotification.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostExitedNotification.java @@ -10,13 +10,17 @@ 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; /** * Reports a supervised listener's hosting-task termination and cleanup outcome. * + * @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) diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetConfigurationParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetConfigurationParams.java index 7eb71f2d37..2185af620e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetConfigurationParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetConfigurationParams.java @@ -23,5 +23,5 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostGetConfigurationParams() { +record HostGetConfigurationParams() { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetEnvironmentCredentialsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetEnvironmentCredentialsParams.java index 9d543a9d26..9b67713cba 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetEnvironmentCredentialsParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGetEnvironmentCredentialsParams.java @@ -23,5 +23,5 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostGetEnvironmentCredentialsParams() { +record HostGetEnvironmentCredentialsParams() { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGitHubEnvironmentOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGitHubEnvironmentOptions.java index 065a9f9328..5a7f72d855 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGitHubEnvironmentOptions.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostGitHubEnvironmentOptions.java @@ -10,13 +10,17 @@ 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; /** * GitHub Mission Control registration options. The compute ID is application-owned and stable. * + * @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) @@ -24,6 +28,21 @@ public record HostGitHubEnvironmentOptions( /** Human-readable environment display name. */ @JsonProperty("name") String name, /** Stable application installation identity, reused across host restarts. */ - @JsonProperty("computeId") String computeId + @JsonProperty("computeId") String computeId, + /** 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. */ + @JsonProperty("requireConnectionBinding") Boolean requireConnectionBinding ) { + + /** + * Creates environment options using the default connection-binding policy. + * + * @param name Human-readable environment display name. + * @param computeId Stable application installation identity, reused across host restarts. + */ + public HostGitHubEnvironmentOptions( + String name, + String computeId + ) { + this(name, computeId, null); + } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostListSessionsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostListSessionsParams.java new file mode 100644 index 0000000000..0e0c2472d0 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostListSessionsParams.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; + +/** + * Reads a running host's catalog or the runtime's saved default catalog. + * + * @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 HostListSessionsParams( + /** Listener UUID returned by host.start; the caller must own it. Omit to read +the saved default compute-identity catalog without starting a listener. */ + @JsonProperty("hostId") String hostId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostListSessionsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostListSessionsResult.java new file mode 100644 index 0000000000..e430b96c29 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostListSessionsResult.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 complete live and dormant advertised catalog, without subscribing to sessions. + * + * @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 HostListSessionsResult( + /** Complete advertised session summaries, in the host's catalog order. */ + @JsonProperty("sessions") List sessions +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostLocalServerConfiguration.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostLocalServerConfiguration.java index 87fb7c8db7..af868879ea 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostLocalServerConfiguration.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostLocalServerConfiguration.java @@ -10,17 +10,21 @@ 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; /** * Normalized local WebSocket listener settings. * + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ +@CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostLocalServerConfiguration( +record HostLocalServerConfiguration( /** Hostname or IP address to bind. */ @JsonProperty("hostname") String hostname, /** Port to bind, with zero requesting OS allocation. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostLocalServerOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostLocalServerOptions.java index b85fe85722..7b3aae19e2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostLocalServerOptions.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostLocalServerOptions.java @@ -10,13 +10,17 @@ 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; /** * Local WebSocket transport options. * + * @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) diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostPublishSessionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostPublishSessionResult.java index 9c276c70cd..29c0600843 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostPublishSessionResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostPublishSessionResult.java @@ -16,7 +16,8 @@ /** * The existing runtime identity and its resource on the listener. * - * @apiNote This method is experimental and may change in a future version. + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ @CopilotExperimental diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostReadyParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostReadyParams.java index 873b77028d..8d1ed23bb6 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostReadyParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostReadyParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostReadyParams( +record HostReadyParams( /** Actual bound WebSocket URL. */ @JsonProperty("address") String address, /** Configured secret token, absent when authentication is disabled. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostRegisterSessionRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostRegisterSessionRequest.java index d121dcad3e..1346edb7b5 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostRegisterSessionRequest.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostRegisterSessionRequest.java @@ -10,18 +10,22 @@ 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; /** * Listener-scoped registration, not a copy or durable adoption of a session. * + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ +@CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostRegisterSessionRequest( +record HostRegisterSessionRequest( /** Canonical ID of the existing resident runtime session. */ @JsonProperty("sessionId") String sessionId, /** Absolute working directory of the resident session. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostReleaseSessionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostReleaseSessionParams.java index 26c69fdb2e..894687ca09 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostReleaseSessionParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostReleaseSessionParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostReleaseSessionParams( +record HostReleaseSessionParams( /** Identity of the participation to release without destroying the session. */ @JsonProperty("handoffId") String handoffId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionCreateCallback.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionCreateCallback.java index 7620ef03dc..d609d096dc 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionCreateCallback.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionCreateCallback.java @@ -10,18 +10,22 @@ 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; /** * Application callback routed over its existing SDK connection. * + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ +@CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostSessionCreateCallback( +record HostSessionCreateCallback( /** Listener UUID identifying the owning application's host. */ @JsonProperty("hostId") String hostId, /** Unique identity of the session participation being requested. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostCreateSessionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionCreateResult.java similarity index 90% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostCreateSessionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionCreateResult.java index 90827fd4e1..caf4f3e785 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostCreateSessionResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionCreateResult.java @@ -16,14 +16,15 @@ /** * The resident session the application has materialized on its own connection. * - * @apiNote This method is experimental and may change in a future version. + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ @CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostCreateSessionResult( +record HostSessionCreateResult( /** Runtime session UUID materialized on the application's SDK connection. */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionReleasedNotification.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionReleasedNotification.java index d00385a364..a9266987c1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionReleasedNotification.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionReleasedNotification.java @@ -10,17 +10,21 @@ 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; /** * Releases the original application session object retained for one handoff. * + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ +@CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostSessionReleasedNotification( +record HostSessionReleasedNotification( /** Listener UUID whose application session participation ended. */ @JsonProperty("hostId") String hostId, /** Identity of the handoff retaining the original application session object. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionSummary.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionSummary.java new file mode 100644 index 0000000000..e644a70e56 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostSessionSummary.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 javax.annotation.processing.Generated; + +/** + * Metadata advertised by the AHP host, not a guarantee that history can be resumed. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record HostSessionSummary( + /** Stable AHP resource URI identifying the session on this host. */ + @JsonProperty("resource") String resource, + /** Authoritative title advertised by the host. */ + @JsonProperty("title") String title, + /** Session creation time as an ISO 8601 timestamp. */ + @JsonProperty("createdAt") String createdAt, + /** Last modification time as an ISO 8601 timestamp. */ + @JsonProperty("modifiedAt") String modifiedAt, + /** Unsigned 32-bit AHP session status bitset (0..=4294967295); unknown bits must be preserved. */ + @JsonProperty("status") Long status, + /** Optional activity description advertised by the host. */ + @JsonProperty("activity") String activity +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostStartRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostStartRequest.java new file mode 100644 index 0000000000..b6d4fc3b98 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostStartRequest.java @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All 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.Objects; +import javax.annotation.processing.Generated; + +/** + * Starts a supervised AHP host with at least one explicitly selected transport. + *

+ * 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 HostStartRequest { + + /** Caller-generated UUID identifying this connection-owned listener. */ + @JsonProperty("hostId") + private final String hostId; + + /** Enables a local WebSocket listener. */ + @JsonProperty("localServer") + private HostLocalServerOptions localServer; + + /** Registers a GitHub Mission Control environment and enables its relay transport. */ + @JsonProperty("githubEnvironment") + private HostGitHubEnvironmentOptions gitHubEnvironment; + + /** Ask the owning SDK application to materialize AHP sessions. */ + @JsonProperty("sessionFactory") + private Boolean sessionFactory; + + /** Ask the owning application to resume its durable AHP sessions. */ + @JsonProperty("resumeFactory") + private Boolean resumeFactory; + + /** Stable application identity selecting its durable host catalog. Defaults to the persisted runtime compute GUID; must agree with githubEnvironment.computeId when both are supplied. */ + @JsonProperty("computeId") + private String computeId; + + /** + * Creates a request with its required inputs. + * + * @param hostId Caller-generated UUID identifying this connection-owned listener. + */ + public HostStartRequest(String hostId) { + this.hostId = Objects.requireNonNull(hostId, "hostId"); + } + + /** + * Returns the {@code hostId} property. + * + * @return Caller-generated UUID identifying this connection-owned listener. + */ + public String getHostId() { + return hostId; + } + + /** + * Returns the {@code localServer} property. + * + * @return Enables a local WebSocket listener. + */ + public HostLocalServerOptions getLocalServer() { + return localServer; + } + + /** + * Returns the {@code githubEnvironment} property. + * + * @return Registers a GitHub Mission Control environment and enables its relay transport. + */ + public HostGitHubEnvironmentOptions getGitHubEnvironment() { + return gitHubEnvironment; + } + + /** + * Returns the {@code sessionFactory} property. + * + * @return Ask the owning SDK application to materialize AHP sessions. + */ + public Boolean getSessionFactory() { + return sessionFactory; + } + + /** + * Returns the {@code resumeFactory} property. + * + * @return Ask the owning application to resume its durable AHP sessions. + */ + public Boolean getResumeFactory() { + return resumeFactory; + } + + /** + * Returns the {@code computeId} property. + * + * @return Stable application identity selecting its durable host catalog. Defaults to the persisted runtime compute GUID; must agree with githubEnvironment.computeId when both are supplied. + */ + public String getComputeId() { + return computeId; + } + + /** + * Sets the {@code localServer} property. + * + * @param value Enables a local WebSocket listener. + * @return this request + */ + public HostStartRequest setLocalServer(HostLocalServerOptions value) { + this.localServer = value; + return this; + } + + /** + * Sets the {@code githubEnvironment} property. + * + * @param value Registers a GitHub Mission Control environment and enables its relay transport. + * @return this request + */ + public HostStartRequest setGitHubEnvironment(HostGitHubEnvironmentOptions value) { + this.gitHubEnvironment = value; + return this; + } + + /** + * Sets the {@code sessionFactory} property. + * + * @param value Ask the owning SDK application to materialize AHP sessions. + * @return this request + */ + public HostStartRequest setSessionFactory(Boolean value) { + this.sessionFactory = value; + return this; + } + + /** + * Sets the {@code resumeFactory} property. + * + * @param value Ask the owning application to resume its durable AHP sessions. + * @return this request + */ + public HostStartRequest setResumeFactory(Boolean value) { + this.resumeFactory = value; + return this; + } + + /** + * Sets the {@code computeId} property. + * + * @param value Stable application identity selecting its durable host catalog. Defaults to the persisted runtime compute GUID; must agree with githubEnvironment.computeId when both are supplied. + * @return this request + */ + public HostStartRequest setComputeId(String value) { + this.computeId = value; + return this; + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LoggedInUser.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LoggedInUser.java new file mode 100644 index 0000000000..ebd6526dc4 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/LoggedInUser.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; + +/** + * 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. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record LoggedInUser( + /** Host the account belongs to, such as `github.com` or an Enterprise server. */ + @JsonProperty("host") String host, + /** Account login on that host. */ + @JsonProperty("login") String login, + /** Account kind, when the host recorded one. Consumers must tolerate new strings. */ + @JsonProperty("kind") String kind, + /** Source account this account was derived from, when one was recorded. */ + @JsonProperty("derivedFrom") String derivedFrom +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryEntry.java similarity index 65% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryEntry.java index 193dea1b4c..9fb16e5a55 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryEntry.java @@ -14,19 +14,22 @@ import javax.annotation.processing.Generated; /** - * Result of registering or re-arming a scheduled prompt. + * Retry outcome for one plugin required by managed settings. + * + * @apiNote This type is experimental and may change in a future version. * - * @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 SessionScheduleAddCronResult( - /** The registered or updated schedule entry. */ - @JsonProperty("entry") ScheduleEntry entry, - /** User-facing validation error, when registration failed. */ +public record ManagedPluginRetryEntry( + /** Plugin spec in "plugin@marketplace" form. */ + @JsonProperty("spec") String spec, + /** What the retry did for this plugin. */ + @JsonProperty("status") ManagedPluginRetryStatus status, + /** Why the plugin was not prepared. Present when `status` is `failed` or `deferred`. */ @JsonProperty("error") String error ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryStatus.java new file mode 100644 index 0000000000..43a46d2625 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedPluginRetryStatus.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * What retrying did for one plugin required by managed settings. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ManagedPluginRetryStatus { + /** The {@code installed} variant. */ + INSTALLED("installed"), + /** The {@code updated} variant. */ + UPDATED("updated"), + /** The {@code already_present} variant. */ + ALREADY_PRESENT("already_present"), + /** The {@code failed} variant. */ + FAILED("failed"), + /** The {@code deferred} variant. */ + DEFERRED("deferred"), + /** The {@code not_required} variant. */ + NOT_REQUIRED("not_required"); + + private final String value; + ManagedPluginRetryStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ManagedPluginRetryStatus fromValue(String value) { + for (ManagedPluginRetryStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ManagedPluginRetryStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingMeta.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingMeta.java index 46967ea441..3a2bf1fd0b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingMeta.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingMeta.java @@ -28,6 +28,8 @@ public record ManagedSettingMeta( /** Whether users and repositories may choose a different value. `false` means policy locks the value. */ @JsonProperty("overridable") Boolean overridable, /** 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. */ - @JsonProperty("source") String source + @JsonProperty("source") String source, + /** Original managed value when the runtime adjusted it to a supported effective value. Omitted when no adjustment was needed. */ + @JsonProperty("requested") String requested ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsMeta.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsMeta.java index 7348141749..d98d081e63 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsMeta.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsMeta.java @@ -28,6 +28,10 @@ public record ManagedSettingsMeta( /** Lock state and provenance of `values.model`. */ @JsonProperty("model") ManagedSettingMeta model, /** Lock state and provenance of `values.autoTier`. */ - @JsonProperty("autoTier") ManagedSettingMeta autoTier + @JsonProperty("autoTier") ManagedSettingMeta autoTier, + /** Lock state and provenance of `values.effortLevel`. */ + @JsonProperty("effortLevel") ManagedSettingMeta effortLevel, + /** Lock state and provenance of `values.contextTier`. */ + @JsonProperty("contextTier") ManagedSettingMeta contextTier ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveParams.java index ffb5af2b18..1ecc14a56c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsResolveParams.java @@ -29,6 +29,23 @@ public record ManagedSettingsResolveParams( /** GitHub token to resolve instead of the current account. The call fails when the token cannot be resolved. */ @JsonProperty("gitHubToken") String gitHubToken, /** Embedding client identity for server policy requests, as in session creation. Omit for the CLI identity. */ - @JsonProperty("clientName") String clientName + @JsonProperty("clientName") String clientName, + /** Working directory used to run an organization policy helper. When omitted, sessionless resolution does not run the helper. */ + @JsonProperty("workingDirectory") String workingDirectory ) { + + /** + * Creates managed-settings resolution parameters without a working directory. + * + * @param selectionId Opaque account identifier returned by `account.getAllUsers`. When omitted, the current account is used, or device policy only when no account is signed in. + * @param gitHubToken GitHub token to resolve instead of the current account. The call fails when the token cannot be resolved. + * @param clientName Embedding client identity for server policy requests, as in session creation. Omit for the CLI identity. + */ + public ManagedSettingsResolveParams( + String selectionId, + String gitHubToken, + String clientName + ) { + this(selectionId, gitHubToken, clientName, null); + } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsValues.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsValues.java index 931294b19c..a9094de7ca 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsValues.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ManagedSettingsValues.java @@ -25,9 +25,13 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record ManagedSettingsValues( - /** Managed default model identifier, as configured. New sessions start with it; it can name a model the account cannot use, so hosts match it against the listed models. */ + /** 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. */ @JsonProperty("model") String model, /** Managed Auto routing preference, used when the selected model is `auto`. */ - @JsonProperty("autoTier") AutoTier autoTier + @JsonProperty("autoTier") AutoTier autoTier, + /** Managed reasoning-effort default for the managed concrete model. The runtime clamps it to an entitled effort when model availability is known. */ + @JsonProperty("effortLevel") String effortLevel, + /** Managed context-tier default for the managed concrete model. */ + @JsonProperty("contextTier") ContextTier contextTier ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAllowedServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAllowedServer.java index 1d0a17cc9b..165559f7f8 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAllowedServer.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpAllowedServer.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record McpAllowedServer( +record McpAllowedServer( /** Allowed server name */ @JsonProperty("name") String name, /** PII-free note explaining why the server was allowed */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigureGitHubResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigureGitHubResult.java new file mode 100644 index 0000000000..bebf76808f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfigureGitHubResult.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; + +/** + * Result of configuring GitHub MCP. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record McpConfigureGitHubResult( + /** Whether GitHub MCP configuration changed. */ + @JsonProperty("changed") Boolean changed +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfiguredServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfiguredServer.java new file mode 100644 index 0000000000..4e37d915f8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfiguredServer.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Effective MCP configuration entry. Configuration enablement is distinct from the optional live observation. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record McpConfiguredServer( + /** Server name (config key) */ + @JsonProperty("name") String name, + /** Whether this configured server is enabled after session configuration and policy filtering. */ + @JsonProperty("enabled") Boolean enabled, + /** Configuration provenance: user, workspace, plugin, builtin, or managed. */ + @JsonProperty("source") McpServerSource source, + /** Plugin name that provided this server, when source is plugin. */ + @JsonProperty("sourcePlugin") String sourcePlugin, + /** Plugin version that provided this server, when source is plugin. */ + @JsonProperty("sourcePluginVersion") String sourcePluginVersion, + /** Human-readable display name supplied by configuration. */ + @JsonProperty("displayName") String displayName, + /** Observed state from an already materialized matching server. Omitted when no live graph has this configured server; it never determines configuration enablement. */ + @JsonProperty("live") McpConfiguredServerState live +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfiguredServerState.java similarity index 68% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfiguredServerState.java index 65f8745baf..0087f2546b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpConfiguredServerState.java @@ -14,19 +14,20 @@ import javax.annotation.processing.Generated; /** - * Result of registering or re-arming a scheduled prompt. + * Observational state for a matching already materialized MCP server. + * + * @apiNote This type is experimental and may change in a future version. * - * @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 SessionScheduleAddSelfPacedResult( - /** The registered or updated schedule entry. */ - @JsonProperty("entry") ScheduleEntry entry, - /** User-facing validation error, when registration failed. */ +public record McpConfiguredServerState( + /** Observed connection status. This is not a configuration or readiness guarantee. */ + @JsonProperty("status") McpServerStatus status, + /** Observed connection error, when the materialized server failed. */ @JsonProperty("error") String error ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFailedServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFailedServer.java index 36009e4915..13ea4c6135 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFailedServer.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFailedServer.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record McpFailedServer( +record McpFailedServer( /** The config key of the server that failed to connect. */ @JsonProperty("name") String name, /** The captured connection failure detail. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFilteredServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFilteredServer.java index e0ecefae76..60f62325ae 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFilteredServer.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpFilteredServer.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record McpFilteredServer( +record McpFilteredServer( /** Filtered server name */ @JsonProperty("name") String name, /** Human-readable filter reason */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPrompt.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPrompt.java new file mode 100644 index 0000000000..b6458bb582 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPrompt.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 java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * An MCP prompt descriptor. Server-provided non-standard fields are exposed under `additionalProperties`. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record McpPrompt( + /** The programmatic name of the prompt */ + @JsonProperty("name") String name, + /** Human-readable display title */ + @JsonProperty("title") String title, + /** Description of what this prompt provides */ + @JsonProperty("description") String description, + /** Arguments accepted by the prompt */ + @JsonProperty("arguments") List arguments, + /** Icons associated with this prompt */ + @JsonProperty("icons") List icons, + /** Prompt-level metadata */ + @JsonProperty("_meta") Map meta, + /** Server-provided non-standard descriptor fields */ + @JsonProperty("additionalProperties") Map additionalProperties +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptArgument.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptArgument.java new file mode 100644 index 0000000000..75ce67bbb8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptArgument.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; + +/** + * An argument accepted by an MCP prompt. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record McpPromptArgument( + /** Name of the argument */ + @JsonProperty("name") String name, + /** Description of the argument */ + @JsonProperty("description") String description, + /** Whether the argument is required; omission is distinct from false */ + @JsonProperty("required") Boolean required, + /** Argument-level metadata */ + @JsonProperty("_meta") Map meta, + /** Server-provided non-standard argument fields */ + @JsonProperty("additionalProperties") Map additionalProperties +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptIcon.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptIcon.java new file mode 100644 index 0000000000..0c4b7f0d4e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptIcon.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 java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * An MCP prompt icon with standard size hints and preserved non-standard fields. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record McpPromptIcon( + /** Icon URI */ + @JsonProperty("src") String src, + /** Icon MIME type, when known */ + @JsonProperty("mimeType") String mimeType, + /** Icon sizes, such as `48x48` or `any` */ + @JsonProperty("sizes") List sizes, + /** Theme hint for this icon */ + @JsonProperty("theme") String theme, + /** Server-provided non-standard icon fields */ + @JsonProperty("additionalProperties") Map additionalProperties +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptMessage.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptMessage.java new file mode 100644 index 0000000000..785d050e93 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptMessage.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; + +/** + * An MCP prompt message with opaque JSON content preserved without flattening or content-type filtering. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record McpPromptMessage( + /** The role of the message sender */ + @JsonProperty("role") McpPromptRole role, + /** The original MCP content block, including nested metadata and unfamiliar content types */ + @JsonProperty("content") Object content, + /** Message-level metadata */ + @JsonProperty("_meta") Map meta, + /** Server-provided non-standard message fields */ + @JsonProperty("additionalProperties") Map additionalProperties +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptRole.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptRole.java new file mode 100644 index 0000000000..461ebb3b73 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpPromptRole.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 sender role of an MCP prompt message. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum McpPromptRole { + /** The {@code user} variant. */ + USER("user"), + /** The {@code assistant} variant. */ + ASSISTANT("assistant"); + + private final String value; + McpPromptRole(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static McpPromptRole fromValue(String value) { + for (McpPromptRole v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown McpPromptRole value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServer.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServer.java index f2917ced22..9b93d39830 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServer.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServer.java @@ -27,6 +27,8 @@ public record McpServer( @JsonProperty("status") McpServerStatus status, /** Configuration source: user, workspace, plugin, builtin, or managed */ @JsonProperty("source") McpServerSource source, + /** Configured URL for an HTTP/SSE server, regardless of configuration source. Omitted for local and in-memory servers. */ + @JsonProperty("url") String url, /** Plugin name that provided this server, when source is plugin. */ @JsonProperty("sourcePlugin") String sourcePlugin, /** Plugin version that provided this server, when source is plugin. */ @@ -63,6 +65,6 @@ public McpServer( String error, McpServerMetadata serverMetadata ) { - this(name, status, source, sourcePlugin, sourcePluginVersion, displayName, error, serverMetadata, null); + this(name, status, source, null, sourcePlugin, sourcePluginVersion, displayName, error, serverMetadata, null); } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerSource.java index 72b1503d59..0902ef8812 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerSource.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpServerSource.java @@ -10,7 +10,7 @@ import javax.annotation.processing.Generated; /** - * Configuration source: user, workspace, plugin, builtin, or managed + * Configuration source: user, workspace, plugin, builtin, managed, or account * * @since 1.0.0 */ @@ -25,7 +25,9 @@ public enum McpServerSource { /** The {@code builtin} variant. */ BUILTIN("builtin"), /** The {@code managed} variant. */ - MANAGED("managed"); + MANAGED("managed"), + /** The {@code account} variant. */ + ACCOUNT("account"); private final String value; McpServerSource(String value) { this.value = value; } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpStartServersResult.java similarity index 86% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpStartServersResult.java index eba9a21c11..745eaa6d04 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/McpStartServersResult.java @@ -10,21 +10,18 @@ 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; /** * MCP server startup filtering 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 SessionMcpReloadWithConfigResult( +record McpStartServersResult( /** Servers filtered out before startup */ @JsonProperty("filteredServers") List filteredServers, /** Non-default servers allowed by policy */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Model.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Model.java index 81ffc46a36..63fbd2937a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Model.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/Model.java @@ -31,6 +31,8 @@ public record Model( @JsonProperty("capabilities") ModelCapabilities capabilities, /** 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. */ @JsonProperty("metadata") Map metadata, + /** 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. */ + @JsonProperty("vendor") String vendor, /** Policy state (if applicable) */ @JsonProperty("policy") ModelPolicy policy, /** Billing information */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelArtifactDetails.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelArtifactDetails.java new file mode 100644 index 0000000000..cef8cb97cc --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelArtifactDetails.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 java.util.List; +import javax.annotation.processing.Generated; + +/** + * Provider-reported model artifact metadata. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelArtifactDetails( + /** Artifact format, such as `gguf`. */ + @JsonProperty("format") String format, + /** Primary model family. */ + @JsonProperty("family") String family, + /** Provider-reported model families. */ + @JsonProperty("families") List families, + /** Provider-reported parameter count label. */ + @JsonProperty("parameterSize") String parameterSize, + /** Provider-reported quantization label. */ + @JsonProperty("quantization") String quantization, + /** Provider-reported model architecture. */ + @JsonProperty("architecture") String architecture, + /** Provider-reported tokenizer. */ + @JsonProperty("tokenizer") String tokenizer +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesSupports.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesSupports.java index daaef4fcb4..17b9732e4d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesSupports.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelCapabilitiesSupports.java @@ -21,6 +21,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record ModelCapabilitiesSupports( + /** Whether the model supports provider-native thinking. Independent of configurable reasoning effort; omission means unknown. */ + @JsonProperty("thinking") Boolean thinking, /** Whether this model supports vision/image input */ @JsonProperty("vision") Boolean vision, /** Whether this model supports canonical tool calling */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelClearStartupSeedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelClearStartupSeedResult.java new file mode 100644 index 0000000000..d905811186 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelClearStartupSeedResult.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; + +/** + * Data type {@code ModelClearStartupSeedResult}. + * + * @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 ModelClearStartupSeedResult( + @JsonProperty("cleared") Boolean cleared +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAdapterDescriptor.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAdapterDescriptor.java new file mode 100644 index 0000000000..1491a834d3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAdapterDescriptor.java @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * A normalized model-provider adapter in the session's effective catalog. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderAdapterDescriptor( + /** Stable opaque identity for routing to this adapter. Unique in the effective catalog, independent of live registration generations. */ + @JsonProperty("adapterId") String adapterId, + /** Descriptive provider family, such as `ollama`. Different adapters may have the same family; use adapterId for routing. */ + @JsonProperty("providerKind") String providerKind, + /** Human-readable provider name. */ + @JsonProperty("displayName") String displayName, + /** Attribution for the adapter itself. */ + @JsonProperty("provenance") ModelProviderAttribution provenance, + /** Adapter-declared policy for passive and automatic discovery. */ + @JsonProperty("automaticDiscovery") ModelProviderAutomaticDiscoveryPolicy automaticDiscovery, + /** Operations supported by this provider adapter. */ + @JsonProperty("operations") List operations +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAdapterOperationDescriptor.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAdapterOperationDescriptor.java new file mode 100644 index 0000000000..b859352e1b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAdapterOperationDescriptor.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; + +/** + * An operation supported by a model-provider adapter. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderAdapterOperationDescriptor( + /** Supported operation name: `discover`, `getStatus`, or `models.list`. Unknown names and duplicate declarations are rejected. */ + @JsonProperty("name") String name, + /** 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. */ + @JsonProperty("inputSchema") Object inputSchema +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAttribution.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAttribution.java new file mode 100644 index 0000000000..74072a9f16 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAttribution.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; + +/** + * Contributor attribution, independent of routing identity and authorization. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderAttribution( + /** Kind of component that supplied the adapter. Attribution does not confer authority. */ + @JsonProperty("source") ModelProviderProvenanceSource source, + /** Stable contributor identifier. Required and nonblank for extension and custom sources; optional for built-in and configured sources. Does not grant authority. */ + @JsonProperty("ownerId") String ownerId, + /** Human-readable contributor name, not the adapter display name. */ + @JsonProperty("ownerDisplayName") String ownerDisplayName +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAutomaticDiscoveryMode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAutomaticDiscoveryMode.java new file mode 100644 index 0000000000..1907b5a2a2 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAutomaticDiscoveryMode.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; + +/** + * When the runtime may run an adapter without an explicit user action. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelProviderAutomaticDiscoveryMode { + /** The {@code automatic} variant. */ + AUTOMATIC("automatic"), + /** The {@code configuredOnly} variant. */ + CONFIGUREDONLY("configuredOnly"), + /** The {@code explicit} variant. */ + EXPLICIT("explicit"); + + private final String value; + ModelProviderAutomaticDiscoveryMode(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelProviderAutomaticDiscoveryMode fromValue(String value) { + for (ModelProviderAutomaticDiscoveryMode v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelProviderAutomaticDiscoveryMode value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAutomaticDiscoveryPolicy.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAutomaticDiscoveryPolicy.java new file mode 100644 index 0000000000..f7fd039063 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderAutomaticDiscoveryPolicy.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; + +/** + * Adapter-declared policy that tells clients whether discovery may run automatically. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderAutomaticDiscoveryPolicy( + /** Whether automatic discovery is allowed, limited to configured providers, or explicit-only. */ + @JsonProperty("mode") ModelProviderAutomaticDiscoveryMode mode, + /** Maximum network scope used by this adapter during discovery. */ + @JsonProperty("networkScope") ModelProviderDiscoveryNetworkScope networkScope, + /** 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. */ + @JsonProperty("requiresInput") Boolean requiresInput, + /** True when the adapter must be enabled by a trusted owner, such as a trusted extension, before automatic discovery may run. */ + @JsonProperty("requiresTrust") Boolean requiresTrust +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderConfigurationDisposition.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderConfigurationDisposition.java new file mode 100644 index 0000000000..4c8b078fc4 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderConfigurationDisposition.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; + +/** + * Whether a planned configuration entry is new or already present in the session registry. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelProviderConfigurationDisposition { + /** The {@code create} variant. */ + CREATE("create"), + /** The {@code alreadyConfigured} variant. */ + ALREADYCONFIGURED("alreadyConfigured"); + + private final String value; + ModelProviderConfigurationDisposition(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelProviderConfigurationDisposition fromValue(String value) { + for (ModelProviderConfigurationDisposition v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelProviderConfigurationDisposition value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderDiscoveryNetworkScope.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderDiscoveryNetworkScope.java new file mode 100644 index 0000000000..30d889ee9b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderDiscoveryNetworkScope.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; + +/** + * Network reach an adapter may use during discovery. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelProviderDiscoveryNetworkScope { + /** The {@code none} variant. */ + NONE("none"), + /** The {@code loopbackOnly} variant. */ + LOOPBACKONLY("loopbackOnly"), + /** The {@code configuredEndpointOnly} variant. */ + CONFIGUREDENDPOINTONLY("configuredEndpointOnly"), + /** The {@code localNetwork} variant. */ + LOCALNETWORK("localNetwork"), + /** The {@code internet} variant. */ + INTERNET("internet"); + + private final String value; + ModelProviderDiscoveryNetworkScope(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelProviderDiscoveryNetworkScope fromValue(String value) { + for (ModelProviderDiscoveryNetworkScope v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelProviderDiscoveryNetworkScope value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderInstance.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderInstance.java new file mode 100644 index 0000000000..06db8b4f3d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderInstance.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * A normalized model-provider instance discovered by the runtime. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderInstance( + /** Self-contained reference for subsequent provider operations. */ + @JsonProperty("reference") ModelProviderInstanceReference reference, + /** Human-readable instance name. */ + @JsonProperty("displayName") String displayName, + /** Attribution for the adapter that produced this instance. */ + @JsonProperty("provenance") ModelProviderProvenance provenance, + /** Inference API endpoint when the provider exposes one separately from its management endpoint. */ + @JsonProperty("inferenceEndpoint") String inferenceEndpoint, + /** Provider family to use for inference against this instance. */ + @JsonProperty("inferenceType") ProviderEndpointType inferenceType, + /** Wire API to use for inference against this instance, when required by the provider family. */ + @JsonProperty("inferenceWireApi") ProviderEndpointWireApi inferenceWireApi, + /** Transport to use for inference against this instance. */ + @JsonProperty("inferenceTransport") ProviderEndpointTransport inferenceTransport +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderInstanceReference.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderInstanceReference.java new file mode 100644 index 0000000000..621c26af9d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderInstanceReference.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; + +/** + * Serializable reference to a discovered provider instance. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderInstanceReference( + /** Stable opaque identity of the adapter that owns this reference. Must be present in the target session's effective catalog. */ + @JsonProperty("adapterId") String adapterId, + /** Descriptive provider family. Must match the selected adapter; not a routing key. */ + @JsonProperty("providerKind") String providerKind, + /** Stable instance identifier derived by the provider adapter, such as `ollama:{normalizedEndpoint}`. */ + @JsonProperty("id") String id, + /** 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. */ + @JsonProperty("managementEndpoint") String managementEndpoint +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderOperationOutcome.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderOperationOutcome.java new file mode 100644 index 0000000000..6e10eddd97 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderOperationOutcome.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; + +/** + * Typed provider-operation outcome. Use the code for control flow and the optional message for display. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderOperationOutcome( + /** Machine-readable operation outcome. */ + @JsonProperty("code") ModelProviderOperationOutcomeCode code, + /** Human-readable detail for non-success outcomes. */ + @JsonProperty("message") String message +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderOperationOutcomeCode.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderOperationOutcomeCode.java new file mode 100644 index 0000000000..feab7ef5b8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderOperationOutcomeCode.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 outcome for a provider operation. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelProviderOperationOutcomeCode { + /** The {@code success} variant. */ + SUCCESS("success"), + /** The {@code absent} variant. */ + ABSENT("absent"), + /** The {@code unreachable} variant. */ + UNREACHABLE("unreachable"), + /** The {@code failed} variant. */ + FAILED("failed"); + + private final String value; + ModelProviderOperationOutcomeCode(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelProviderOperationOutcomeCode fromValue(String value) { + for (ModelProviderOperationOutcomeCode v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelProviderOperationOutcomeCode value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderProvenance.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderProvenance.java new file mode 100644 index 0000000000..186a32c91f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderProvenance.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Attribution for the adapter that produced a provider row. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderProvenance( + /** Stable opaque adapter identity from the effective catalog. Treat this as a whole identifier, not a parseable owner or kind. */ + @JsonProperty("adapterId") String adapterId, + /** Descriptive provider family that produced this row; not a routing key. */ + @JsonProperty("providerKind") String providerKind, + /** Kind of component that supplied the adapter. */ + @JsonProperty("source") ModelProviderProvenanceSource source, + /** Stable contributor identifier when the adapter has an owner outside the runtime. Independent of the contribution mechanism and not a routing key. */ + @JsonProperty("ownerId") String ownerId, + /** Human-readable contributor name, not the adapter display name. */ + @JsonProperty("ownerDisplayName") String ownerDisplayName +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderProvenanceSource.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderProvenanceSource.java new file mode 100644 index 0000000000..84e91393b9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderProvenanceSource.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; + +/** + * Kind of component that supplied a provider adapter or row. Attribution does not confer authority. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelProviderProvenanceSource { + /** The {@code builtIn} variant. */ + BUILTIN("builtIn"), + /** The {@code configured} variant. */ + CONFIGURED("configured"), + /** The {@code extension} variant. */ + EXTENSION("extension"), + /** The {@code custom} variant. */ + CUSTOM("custom"); + + private final String value; + ModelProviderProvenanceSource(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelProviderProvenanceSource fromValue(String value) { + for (ModelProviderProvenanceSource v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelProviderProvenanceSource value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderWarning.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderWarning.java new file mode 100644 index 0000000000..deb6f08c78 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelProviderWarning.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 non-fatal provider observation warning. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelProviderWarning( + /** Machine-readable warning code. */ + @JsonProperty("code") String code, + /** Human-readable warning message. */ + @JsonProperty("message") String message +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApplyStartupOverlayResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelSwitchToResult.java similarity index 92% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApplyStartupOverlayResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelSwitchToResult.java index 325b70c8f1..accae12e73 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApplyStartupOverlayResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ModelSwitchToResult.java @@ -10,21 +10,18 @@ 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 model identifier active on the session after the switch. * - * @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 SessionModelApplyStartupOverlayResult( +public record ModelSwitchToResult( /** Stable queue item identifier when this request was enqueued. Remains present if the item drains before the response is returned. */ @JsonProperty("queueId") String queueId, /** Currently active model identifier after the switch */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/NamedProviderConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/NamedProviderConfig.java index dc57bca4f3..acb508a0d5 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/NamedProviderConfig.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/NamedProviderConfig.java @@ -40,6 +40,8 @@ public record NamedProviderConfig( @JsonProperty("azure") ProviderConfigAzure azure, /** Additional HTTP headers included with provider requests. */ @JsonProperty("headers") Map headers, + /** The product serving the provider's models, reported in telemetry as `model_provider`. Only affects telemetry. */ + @JsonProperty("modelProvider") ProviderConfigModelProvider modelProvider, /** Whether the host supplies bearer tokens dynamically. */ @JsonProperty("hasBearerTokenProvider") Boolean hasBearerTokenProvider ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PendingExternalToolRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PendingExternalToolRequest.java new file mode 100644 index 0000000000..6529827e05 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/PendingExternalToolRequest.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; + +/** + * External tool call of the session or one of its sub-agents that is still waiting for session.tools.handlePendingToolCall. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record PendingExternalToolRequest( + /** Request ID to pass to session.tools.handlePendingToolCall */ + @JsonProperty("requestId") String requestId, + /** Tool call ID assigned to this external tool invocation */ + @JsonProperty("toolCallId") String toolCallId, + /** Name of the external tool to invoke */ + @JsonProperty("toolName") String toolName, + /** Arguments to pass to the external tool */ + @JsonProperty("arguments") Object arguments, + /** Stable identity of the provider that offered the tool, for hosts that route extension-owned tools by provider */ + @JsonProperty("providerId") String providerId, + /** 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 */ + @JsonProperty("agentId") String agentId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfig.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfig.java index 22c7414996..4a3c2f7e8b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfig.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfig.java @@ -46,6 +46,8 @@ public record ProviderConfig( @JsonProperty("modelCapabilities") ModelCapabilitiesOverride modelCapabilities, /** Provider name used for model and telemetry attribution. */ @JsonProperty("providerName") String providerName, + /** 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. */ + @JsonProperty("modelProvider") ProviderConfigModelProvider modelProvider, /** The model identifier sent to the provider API for inference (the "wire" model), as opposed to modelId which is the well-known base. */ @JsonProperty("wireModel") String wireModel, /** Maximum prompt/input tokens for the model. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigModelProvider.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigModelProvider.java new file mode 100644 index 0000000000..62e1d74944 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderConfigModelProvider.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; + +/** + * The product serving the model, reported in telemetry as `model_provider`. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderConfigModelProvider { + /** The {@code openai} variant. */ + OPENAI("openai"), + /** The {@code anthropic} variant. */ + ANTHROPIC("anthropic"), + /** The {@code azure_openai} variant. */ + AZURE_OPENAI("azure_openai"), + /** The {@code ollama} variant. */ + OLLAMA("ollama"), + /** The {@code lm_studio} variant. */ + LM_STUDIO("lm_studio"), + /** The {@code foundry_local} variant. */ + FOUNDRY_LOCAL("foundry_local"), + /** The {@code llama_cpp} variant. */ + LLAMA_CPP("llama_cpp"); + + private final String value; + ProviderConfigModelProvider(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderConfigModelProvider fromValue(String value) { + for (ProviderConfigModelProvider v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderConfigModelProvider value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaAccessState.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaAccessState.java new file mode 100644 index 0000000000..d567cb75b3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaAccessState.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; + +/** + * Access policy reported by the quota service. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaAccessState { + /** The {@code allowed} variant. */ + ALLOWED("allowed"), + /** The {@code denied} variant. */ + DENIED("denied"), + /** The {@code not_required} variant. */ + NOT_REQUIRED("not_required"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"); + + private final String value; + ProviderQuotaAccessState(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaAccessState fromValue(String value) { + for (ProviderQuotaAccessState v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaAccessState value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaAcquisitionStatus.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaAcquisitionStatus.java new file mode 100644 index 0000000000..575227472b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaAcquisitionStatus.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; + +/** + * Acquisition state, independent of the service's business access/capacity verdict. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaAcquisitionStatus { + /** The {@code succeeded} variant. */ + SUCCEEDED("succeeded"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"), + /** The {@code failed} variant. */ + FAILED("failed"); + + private final String value; + ProviderQuotaAcquisitionStatus(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaAcquisitionStatus fromValue(String value) { + for (ProviderQuotaAcquisitionStatus v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaAcquisitionStatus value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaBudgetMetadata.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaBudgetMetadata.java new file mode 100644 index 0000000000..2697dbdf48 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaBudgetMetadata.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 javax.annotation.processing.Generated; + +/** + * Authoritative budget measurements and policy metadata, independent of provider. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProviderQuotaBudgetMetadata( + /** Whether the budget has unlimited entitlement. */ + @JsonProperty("unlimited") Boolean unlimited, + /** Exact budget entitlement, retaining incumbent fractional-unit compatibility. */ + @JsonProperty("entitlement") Double entitlement, + /** Exact measured consumption in the budget's unit. */ + @JsonProperty("consumed") Double consumed, + /** Service-reported remaining percentage for authoritative budget presentation. */ + @JsonProperty("remainingPercentage") Double remainingPercentage, + /** Whether service policy allows continued usage after exhaustion. */ + @JsonProperty("usageAllowedWhenExhausted") Boolean usageAllowedWhenExhausted, + /** Usage beyond entitlement, in the budget's unit. */ + @JsonProperty("overage") Double overage, + /** Whether additional usage is allowed when the budget is exhausted. */ + @JsonProperty("overageAllowedWhenExhausted") Boolean overageAllowedWhenExhausted, + /** Reset instant in epoch milliseconds when this budget actually defines a window. */ + @JsonProperty("resetAtEpochMs") Double resetAtEpochMs, + /** Whether the reset instant is an estimate. */ + @JsonProperty("resetEstimated") Boolean resetEstimated, + /** Whether this budget uses token-based billing. */ + @JsonProperty("tokenBasedBilling") Boolean tokenBasedBilling, + /** Optional additional-usage budget cap. */ + @JsonProperty("overageLimit") Double overageLimit +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaCapacityState.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaCapacityState.java new file mode 100644 index 0000000000..45e9bf6a98 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaCapacityState.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; + +/** + * Capacity is independent of whether a numeric balance was supplied. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaCapacityState { + /** The {@code available} variant. */ + AVAILABLE("available"), + /** The {@code exhausted} variant. */ + EXHAUSTED("exhausted"), + /** The {@code unlimited} variant. */ + UNLIMITED("unlimited"), + /** The {@code not_required} variant. */ + NOT_REQUIRED("not_required"), + /** The {@code not_applicable} variant. */ + NOT_APPLICABLE("not_applicable"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"), + /** The {@code unavailable} variant. */ + UNAVAILABLE("unavailable"); + + private final String value; + ProviderQuotaCapacityState(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaCapacityState fromValue(String value) { + for (ProviderQuotaCapacityState v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaCapacityState value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaObservationKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaObservationKind.java new file mode 100644 index 0000000000..ca1406ed70 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaObservationKind.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; + +/** + * Timing and purpose of a provider quota observation. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaObservationKind { + /** The {@code account_snapshot} variant. */ + ACCOUNT_SNAPSHOT("account_snapshot"), + /** The {@code admission_state} variant. */ + ADMISSION_STATE("admission_state"); + + private final String value; + ProviderQuotaObservationKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaObservationKind fromValue(String value) { + for (ProviderQuotaObservationKind v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaObservationKind value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaQuantityKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaQuantityKind.java new file mode 100644 index 0000000000..1282854ba8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaQuantityKind.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; + +/** + * Interpretation permitted for independently reported quantities. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaQuantityKind { + /** The {@code authoritative_budget} variant. */ + AUTHORITATIVE_BUDGET("authoritative_budget"), + /** The {@code advisory_balance} variant. */ + ADVISORY_BALANCE("advisory_balance"), + /** The {@code none} variant. */ + NONE("none"); + + private final String value; + ProviderQuotaQuantityKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaQuantityKind fromValue(String value) { + for (ProviderQuotaQuantityKind v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaQuantityKind value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaState.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaState.java new file mode 100644 index 0000000000..253d64bbe8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaState.java @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * An account quota reading. Absence of a quantity is unknown, never zero. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProviderQuotaState( + /** Provider/account whose service reported this state. */ + @JsonProperty("provider") ModelProviderRef provider, + /** Service-owned quota identifier within this provider. */ + @JsonProperty("quotaId") String quotaId, + /** Unit of entitledQuantity and availableQuantity. */ + @JsonProperty("unit") ProviderQuotaUnit unit, + /** Whether the service permits access, independently of balance. */ + @JsonProperty("accessState") ProviderQuotaAccessState accessState, + /** Service capacity state; missing quantities do not imply exhaustion. */ + @JsonProperty("capacityState") ProviderQuotaCapacityState capacityState, + /** Whether this is a GET account reading or a pre-response admission observation. Observations are never merged across kinds. */ + @JsonProperty("observationKind") ProviderQuotaObservationKind observationKind, + /** Whether quantities are authoritative budget measurements, advisory balances, or absent. Only authoritative budgets support percentage presentation. */ + @JsonProperty("quantityKind") ProviderQuotaQuantityKind quantityKind, + /** Acquisition outcome. Unavailable/failed readings have no service verdict; access/capacity unavailable are compatibility placeholders only. */ + @JsonProperty("acquisitionStatus") ProviderQuotaAcquisitionStatus acquisitionStatus, + /** When the runtime observed this reading, not a charge timestamp or guarantee that consumption has settled. */ + @JsonProperty("observedAt") String observedAt, + /** HTTP status from acquisition, when available. */ + @JsonProperty("httpStatus") Long httpStatus, + /** Service error code or client acquisition category, separate from the business-state reason. */ + @JsonProperty("acquisitionError") String acquisitionError, + /** Optional metadata for an authoritative budget. Advisory balances and admission-only observations do not populate this. */ + @JsonProperty("budgetMetadata") ProviderQuotaBudgetMetadata budgetMetadata, + /** Key for the backwards-compatible snapshots projection, when the authoritative budget supports that contract. */ + @JsonProperty("compatibilityKey") String compatibilityKey, + /** Explicit service admission flag, when reported. */ + @JsonProperty("hasQuota") Boolean hasQuota, + /** Independently reported signed 64-bit entitlement. -1 is an unlimited sentinel, not a capacity-state rewrite; omission and null are preserved. */ + @JsonProperty("entitledQuantity") Long entitledQuantity, + /** Independently reported signed 64-bit available quantity. Zero does not override the service access/capacity verdict; omission and null are preserved. */ + @JsonProperty("availableQuantity") Long availableQuantity, + /** Service-reported explanation for the state. */ + @JsonProperty("reason") String reason, + /** Service-reported quota source. */ + @JsonProperty("source") String source, + /** Service name owning this reading. */ + @JsonProperty("service") String service +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaUnit.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaUnit.java new file mode 100644 index 0000000000..9c9e7f3d3e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ProviderQuotaUnit.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; + +/** + * Units explicitly reported by a quota provider. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ProviderQuotaUnit { + /** The {@code ai_credits} variant. */ + AI_CREDITS("ai_credits"), + /** The {@code requests} variant. */ + REQUESTS("requests"), + /** The {@code tokens} variant. */ + TOKENS("tokens"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"); + + private final String value; + ProviderQuotaUnit(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ProviderQuotaUnit fromValue(String value) { + for (ProviderQuotaUnit v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ProviderQuotaUnit value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueBeginDeferredIdleDrainResult.java similarity index 88% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueBeginDeferredIdleDrainResult.java index 77e6decb9b..a90a855420 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueBeginDeferredIdleDrainResult.java @@ -16,14 +16,15 @@ /** * Whether a deferred-idle drain should run. * - * @apiNote This method is experimental and may change in a future version. + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ @CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueBeginDeferredIdleDrainResult( +record QueueBeginDeferredIdleDrainResult( /** True when the host should run finishDeferredIdleDrain asynchronously. */ @JsonProperty("shouldDrain") Boolean shouldDrain ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueEnqueueResumePendingResult.java similarity index 82% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueEnqueueResumePendingResult.java index 324765b4a2..74e30209fd 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueEnqueueResumePendingResult.java @@ -10,20 +10,17 @@ 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; /** * Result of enqueueing the resume-pending wake item. * - * @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 SessionQueueEnqueueResumePendingResult( +record QueueEnqueueResumePendingResult( /** True when a wake item was newly queued. */ @JsonProperty("queued") Boolean queued ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueFinishDeferredIdleDrainResult.java similarity index 89% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueFinishDeferredIdleDrainResult.java index 1e6cc52577..19bd1c2994 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueFinishDeferredIdleDrainResult.java @@ -16,14 +16,15 @@ /** * Action selected by the native deferred-idle drain. * - * @apiNote This method is experimental and may change in a future version. + * @apiNote This type is experimental and may change in a future version. + * * @since 1.0.0 */ @CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueFinishDeferredIdleDrainResult( +record QueueFinishDeferredIdleDrainResult( /** One of none, processQueue, or emitSessionIdle. */ @JsonProperty("action") String action, /** Whether the deferred idle was caused by an aborted foreground turn. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueHasPendingResult.java similarity index 83% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueHasPendingResult.java index 5373856a45..d71abac9c2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueHasPendingResult.java @@ -10,20 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import com.github.copilot.CopilotExperimental; import javax.annotation.processing.Generated; /** * Whether the native queue has pending work. * - * @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 SessionQueueHasPendingResult( +record QueueHasPendingResult( /** True when queued or immediate native work is pending. */ @JsonProperty("hasPending") Boolean hasPending ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueRemoveMostRecentResult.java similarity index 83% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueRemoveMostRecentResult.java index bbc3715880..ec0ff45f5f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueConsumeSystemNotificationsResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueRemoveMostRecentResult.java @@ -10,20 +10,17 @@ 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; /** * Indicates whether a user-facing pending item was removed. * - * @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 SessionQueueConsumeSystemNotificationsResult( +public record QueueRemoveMostRecentResult( /** True if a user-facing pending item was removed (LIFO across both queues); false when no removable items remained. */ @JsonProperty("removed") Boolean removed ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueSnapshotResult.java similarity index 89% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueSnapshotResult.java index 708d9ef1df..6712e014c5 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QueueSnapshotResult.java @@ -10,21 +10,18 @@ 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; /** * Internal snapshot of native queue state for local session orchestration. * - * @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 SessionQueueSnapshotResult( +record QueueSnapshotResult( /** Queue item identifier of a model switch that has been dequeued but not yet applied. */ @JsonProperty("inFlightModelChangeId") String inFlightModelChangeId, /** User-facing pending items in FIFO order. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QuotaWarningProjection.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QuotaWarningProjection.java new file mode 100644 index 0000000000..dad4961084 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/QuotaWarningProjection.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; + +/** + * A pending session quota warning, consumed once by takeWarnings. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record QuotaWarningProjection( + /** Existing warning category. */ + @JsonProperty("warningType") String warningType, + /** User-facing warning text. */ + @JsonProperty("message") String message, + /** Related account or upgrade link. */ + @JsonProperty("url") String url +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxHostCapability.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxHostCapability.java index e3efc94513..2c4925f402 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxHostCapability.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxHostCapability.java @@ -21,7 +21,7 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record SandboxHostCapability( - /** 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, and a policy that uses it must also set `network.allowLocalNetwork`), `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). */ + /** 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). */ @JsonProperty("name") String name, /** Whether this host can run the feature. */ @JsonProperty("supported") Boolean supported, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaCreateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaCreateParams.java new file mode 100644 index 0000000000..ac0872b397 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaCreateParams.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 SandboxProxyCaCreateParams( + /** 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/SandboxProxyCaCreateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaCreateResult.java new file mode 100644 index 0000000000..a4d2f12cc2 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaCreateResult.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; + +/** + * Result of creating the persistent certificate authority of the sandbox credential proxy. + * + * @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 SandboxProxyCaCreateResult( + /** Absolute path of the public certificate of the certificate authority, in PEM format. */ + @JsonProperty("certificatePath") String certificatePath +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaGetStatusParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaGetStatusParams.java new file mode 100644 index 0000000000..6f654fb053 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaGetStatusParams.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 SandboxProxyCaGetStatusParams( + /** 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/SandboxProxyCaGetStatusResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaGetStatusResult.java new file mode 100644 index 0000000000..c01ac9077d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaGetStatusResult.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; + +/** + * Status of the persistent certificate authority of the sandbox credential proxy. + * + * @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 SandboxProxyCaGetStatusResult( + /** The state of the certificate authority. */ + @JsonProperty("state") SandboxProxyCaState state, + /** Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. */ + @JsonProperty("detail") String detail, + /** 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. */ + @JsonProperty("canInstall") Boolean canInstall +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaRemoveResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaRemoveResult.java new file mode 100644 index 0000000000..8e6dd46d30 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaRemoveResult.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; + +/** + * Status of the persistent certificate authority of the sandbox credential proxy. + * + * @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 SandboxProxyCaRemoveResult( + /** The state of the certificate authority. */ + @JsonProperty("state") SandboxProxyCaState state, + /** Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. */ + @JsonProperty("detail") String detail, + /** 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. */ + @JsonProperty("canInstall") Boolean canInstall +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaRotateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaRotateParams.java new file mode 100644 index 0000000000..2965bb3151 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaRotateParams.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 SandboxProxyCaRotateParams( + /** 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/SandboxProxyCaRotateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaRotateResult.java new file mode 100644 index 0000000000..b53cc5aa68 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaRotateResult.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; + +/** + * Status of the persistent certificate authority of the sandbox credential proxy. + * + * @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 SandboxProxyCaRotateResult( + /** The state of the certificate authority. */ + @JsonProperty("state") SandboxProxyCaState state, + /** Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. */ + @JsonProperty("detail") String detail, + /** 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. */ + @JsonProperty("canInstall") Boolean canInstall +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaState.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaState.java new file mode 100644 index 0000000000..4a4dc15662 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaState.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; + +/** + * State of the persistent certificate authority of the sandbox credential proxy. + * + * @apiNote This type is experimental and may change in a future version. + * + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum SandboxProxyCaState { + /** The {@code unsupported} variant. */ + UNSUPPORTED("unsupported"), + /** The {@code notInstalled} variant. */ + NOTINSTALLED("notInstalled"), + /** The {@code installed} variant. */ + INSTALLED("installed"), + /** The {@code error} variant. */ + ERROR("error"); + + private final String value; + SandboxProxyCaState(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static SandboxProxyCaState fromValue(String value) { + for (SandboxProxyCaState v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown SandboxProxyCaState value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaTrustParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaTrustParams.java new file mode 100644 index 0000000000..b54e2ce93e --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaTrustParams.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 SandboxProxyCaTrustParams( + /** 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/SandboxProxyCaTrustResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaTrustResult.java new file mode 100644 index 0000000000..24e462e16b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SandboxProxyCaTrustResult.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; + +/** + * Status of the persistent certificate authority of the sandbox credential proxy. + * + * @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 SandboxProxyCaTrustResult( + /** The state of the certificate authority. */ + @JsonProperty("state") SandboxProxyCaState state, + /** Human-readable reason for the state. On `installed` or `notInstalled`, present only when the certificate authority must be rotated, and then says why. */ + @JsonProperty("detail") String detail, + /** 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. */ + @JsonProperty("canInstall") Boolean canInstall +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ScheduleAddResult.java similarity index 84% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ScheduleAddResult.java index 021c784c37..9df295e3d8 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ScheduleAddResult.java @@ -10,20 +10,17 @@ 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; /** * Result of registering or re-arming a scheduled prompt. * - * @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 SessionScheduleAddResult( +record ScheduleAddResult( /** The registered or updated schedule entry. */ @JsonProperty("entry") ScheduleEntry entry, /** User-facing validation error, when registration failed. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ScheduleHasSelfPacedResult.java similarity index 83% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ScheduleHasSelfPacedResult.java index 84c8e7a501..daf5830d1e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ScheduleHasSelfPacedResult.java @@ -10,20 +10,17 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; -import com.github.copilot.CopilotExperimental; import javax.annotation.processing.Generated; /** * Whether the session currently has an active self-paced schedule. * - * @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 SessionScheduleHasSelfPacedResult( +record ScheduleHasSelfPacedResult( /** True when at least one active schedule is self-paced. */ @JsonProperty("hasSelfPaced") Boolean hasSelfPaced ) { 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 0adbf18092..714ef4ebb6 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 @@ -28,7 +28,7 @@ public final class ServerAccountApi { } /** - * Optional opaque account selection or compatibility GitHub token used to look up quota. + * Gets Copilot quota usage for the current or opaquely selected authenticated user. *

* Invokes the method with no params, applying the runtime defaults. * @@ -41,7 +41,7 @@ public CompletableFuture getQuota() { } /** - * Optional opaque account selection or compatibility GitHub token used to look up quota. + * Gets Copilot quota usage for the current or opaquely selected authenticated user. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -52,7 +52,7 @@ public CompletableFuture getQuota(AccountGetQuotaParams p } /** - * Current authentication state + * Gets the currently active authentication credentials from the global auth manager. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -63,7 +63,7 @@ public CompletableFuture getCurrentAuth() { } /** - * List of all authenticated users + * Gets all authenticated users available for account switching. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -74,7 +74,7 @@ public CompletableFuture> getAllUsers() { } /** - * Credentials to validate and store. Omit login to resolve the authenticated user from the token. + * Validates and stores authentication credentials. When login is omitted, resolves the authenticated user from the token before persistence. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -85,7 +85,7 @@ public CompletableFuture login(AccountLoginParams params) { } /** - * User to log out + * Removes user authentication from keychain and persisted state. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentRegistryApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentRegistryApi.java index f398fb2dcb..a96ce680d7 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentRegistryApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentRegistryApi.java @@ -27,7 +27,7 @@ public final class ServerAgentRegistryApi { } /** - * Inputs to spawn a managed-server child via the controller's spawn delegate. + * Spawns a managed-server child with the supplied configuration and returns a discriminated-union result. The caller (typically the CLI controller) is responsible for attaching to the spawned child and sending any follow-up prompt. When the controller-local spawn gate is closed the server returns JSON-RPC MethodNotFound. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAgentsApi.java index 0c621a3624..ddff436555 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 @@ -27,7 +27,7 @@ public final class ServerAgentsApi { } /** - * Optional project paths to include in agent discovery. + * Discovers custom agents across user, project, plugin, and remote sources. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture discover(AgentsDiscoverParams par } /** - * Optional project paths to include when enumerating agent discovery directories. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -48,4 +48,59 @@ public CompletableFuture getDiscoveryPaths(Agents return caller.invoke("agents.getDiscoveryPaths", params, 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getBuiltins() { + return caller.invoke("agents.getBuiltins", java.util.Map.of(), AgentsGetBuiltinsResult.class); + } + + /** + * Lists the shipped agents a client should offer right now, filtered by the feature flags it passes. `getBuiltins` names every agent the runtime knows about; some of those are gated, so a client rendering a picker wants this narrower list together with the description to show beside each name. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getAvailableBuiltins(AgentsGetAvailableBuiltinsParams params) { + return caller.invoke("agents.getAvailableBuiltins", params, AgentsGetAvailableBuiltinsResult.class); + } + + /** + * Loads one shipped agent's YAML definition, for a client that needs what the agent declares rather than only its name. `getBuiltins` reports which names have a definition to load: a name outside its `yamlBasedNames` is special-cased in code and has none. The definition crosses as its own JSON rather than as contract-typed fields, because the runtime parses it with the agent schema's tolerant shape and re-typing it here would drop the keys that shape accepts and this one does not. The projected `__nativeCustomAgent` view the runtime derives is included, so a caller reading the declared model and a caller rendering the agent see the same definition. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getBuiltinDefinition(AgentsGetBuiltinDefinitionParams params) { + return caller.invoke("agents.getBuiltinDefinition", params, AgentsGetBuiltinDefinitionResult.class); + } + + /** + * Projects one shipped agent the way a picker lists it, reading only the metadata at the head of the definition file and stopping before the prompt body. `getBuiltinDefinition` answers the whole definition instead, so a client listing every shipped agent should prefer this one: the cost of a listing grows with the number of agents, and the prompt body is the part a listing never shows. The two also differ in shape. This returns the projected custom agent on its own, whereas `getBuiltinDefinition` returns the authored definition with that projection nested under `__nativeCustomAgent`. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture getBuiltinListingDefinition(AgentsGetBuiltinListingDefinitionParams params) { + return caller.invoke("agents.getBuiltinListingDefinition", params, AgentsGetBuiltinListingDefinitionResult.class); + } + + /** + * Resolves the model a custom agent asks for against the models actually available, and answers both the model to switch to and the warning a user should see when the agent's preference cannot be met. A custom agent may name several acceptable models in preference order, so the decision is a match rather than a lookup, and an agent whose preference is unavailable is a normal outcome that produces a warning rather than an error. A host must call this rather than pick the first available name itself, because the preference order and the wording of the warning are what keep one installation's agent selection the same as another's. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture customAgentInitialModelDecision(AgentsCustomAgentInitialModelDecisionParams params) { + return caller.invoke("agents.customAgentInitialModelDecision", params, AgentsCustomAgentInitialModelDecisionResult.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCatalogApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCatalogApi.java index 32e7963e35..f7d7c2fe1b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCatalogApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCatalogApi.java @@ -28,7 +28,7 @@ public final class ServerCatalogApi { } /** - * 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. + * Requests a bounded catalog search. 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 search available returns inert candidate summaries, each with an opaque single-use handle scoped to this runtime instance; a runtime without it returns the typed search-unavailable result. Public authorities may be searched anonymously, while an authority that requires credentials yields the typed authentication-required result. All returned text, URLs, and package metadata are untrusted external data and can never trigger instructions, tools, or installation. Read-only: nothing is installed, configured, or persisted. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -39,7 +39,7 @@ public CompletableFuture search(CatalogSearchParams params) } /** - * 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. + * Requests a bounded catalog search. 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 search available returns inert candidate summaries, each with an opaque single-use handle scoped to this runtime instance; a runtime without it returns the typed search-unavailable result. Public authorities may be searched anonymously, while an authority that requires credentials yields the typed authentication-required result. All returned text, URLs, and package metadata are untrusted external data and can never trigger instructions, tools, or installation. Read-only: nothing is installed, configured, or persisted. *

* Accepts the extensible request, including inputs added after the params record. * @@ -52,7 +52,7 @@ public CompletableFuture search(CatalogSearchRequest reques } /** - * Terminates one retained catalog selection group through an opaque reference previously returned by the model-safe search projection. + * Terminates one retained catalog selection group. A selected outcome returns the native host a fresh single-use candidate handle plus the original searchId for a later explicit mcp.planInstall call; non-selected outcomes release the group without producing a planning input. Candidate state, cards, URLs, credentials and private identifiers remain inside the runtime. The model-facing catalog_select tool projects the result separately and never exposes the candidate handle or searchId. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCommandsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCommandsApi.java index efa5317ea4..82c5fad8c4 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCommandsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerCommandsApi.java @@ -27,7 +27,7 @@ public final class ServerCommandsApi { } /** - * Slash commands available in the session, after applying any include/exclude filters. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerConnectorsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerConnectorsApi.java new file mode 100644 index 0000000000..5b74d83721 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerConnectorsApi.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 connectors} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerConnectorsApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerConnectorsApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Returns feature availability. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture getCapabilities() { + return caller.invoke("connectors.getCapabilities", java.util.Map.of(), ConnectorsGetCapabilitiesResult.class); + } + + /** + * Returns eligible accounts. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture getAccounts() { + return caller.invoke("connectors.getAccounts", java.util.Map.of(), ConnectorsGetAccountsResult.class); + } + + /** + * Lists entries for the selected account. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture list(ConnectorsListParams params) { + return caller.invoke("connectors.list", params, ConnectorsListResult.class); + } + + /** + * Refreshes entries for the selected account. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture refresh(ConnectorsRefreshParams params) { + return caller.invoke("connectors.refresh", params, ConnectorsRefreshResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerEnvironmentsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerEnvironmentsApi.java index 9d52f2f1ea..a8220ef6fc 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerEnvironmentsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerEnvironmentsApi.java @@ -27,7 +27,7 @@ public final class ServerEnvironmentsApi { } /** - * Optional discovery filters supported by GitHub Mission Control. + * Lists GitHub Mission Control environments visible to the authenticated identity. Does not require a running host and excludes host relay credentials. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture list(EnvironmentsListParams par } /** - * Identify a Mission Control environment to retrieve. + * Gets safe discovery information for a GitHub Mission Control environment without requiring a running host. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,7 +49,7 @@ public CompletableFuture get(EnvironmentsGetParams params } /** - * Identify a user-managed Mission Control environment to delete. + * Deletes a user-managed GitHub Mission Control environment. GitHub-managed environments cannot be deleted. Does not stop a running host, which may register again. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java index 7bc74b441f..385fc3bbc2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerExtensionsApi.java @@ -27,7 +27,7 @@ public final class ServerExtensionsApi { } /** - * Extensions discovered from persisted Copilot home state and their effective loading mode. Launch-scoped additional plugins are not included. + * Discovers user and enabled installed-plugin extensions from persisted Copilot home state, including enablement preferences. Launch-scoped additional plugins are not included. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture discover() { } /** - * Source-qualified extension identifiers to persistently enable for future sessions. + * Persistently enables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.enable to update them. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,7 +49,7 @@ public CompletableFuture enable(ExtensionsEnableParams params) { } /** - * Source-qualified extension identifiers to persistently disable for future sessions. + * Persistently disables extension IDs for future sessions. Active sessions are unchanged; use session.extensions.disable to update them. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGitApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGitApi.java new file mode 100644 index 0000000000..da512566d0 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGitApi.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code git} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerGitApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerGitApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture currentBranchRemote(GitCurrentBranchRemoteParams params) { + return caller.invoke("git.currentBranchRemote", params, GitCurrentBranchRemoteResult.class); + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture workingDirectoryContext(GitWorkingDirectoryContextParams params) { + return caller.invoke("git.workingDirectoryContext", params, SessionWorkingDirectoryContext.class); + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture reposFromRemotes(GitReposFromRemotesParams params) { + return caller.invoke("git.reposFromRemotes", params, GitReposFromRemotesResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGitHubOwnersApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGitHubOwnersApi.java new file mode 100644 index 0000000000..175c41400a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGitHubOwnersApi.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code gitHubOwners} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerGitHubOwnersApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerGitHubOwnersApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture nextRequestId() { + return caller.invoke("gitHubOwners.nextRequestId", java.util.Map.of(), GitHubOwnersRequestIdResult.class); + } + + /** + * 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`. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture list(GitHubOwnersListParams params) { + return caller.invoke("gitHubOwners.list", params, GitHubOwnersListResult.class); + } + + /** + * Abandons an owner listing started with the given request id. Answers `canceled: true` while a listing with that id is running. Answers `canceled: false` when the id was never registered, was registered but not used, was released after being abandoned, or its listing has ended. Canceling an unused id releases it, and a later `list` with that id is refused. The cancel acts only on owner listings and never reaches another request of the host. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture cancel(GitHubOwnersCancelParams params) { + return caller.invoke("gitHubOwners.cancel", params, GitHubOwnersCancelResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAccountsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGitHubRepositoryApi.java similarity index 53% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAccountsApi.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGitHubRepositoryApi.java index 20473515d2..d3eb8f0bd0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerAccountsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGitHubRepositoryApi.java @@ -12,29 +12,29 @@ import javax.annotation.processing.Generated; /** - * API methods for the {@code accounts} namespace. + * API methods for the {@code gitHubRepository} namespace. * * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public final class ServerAccountsApi { +final class ServerGitHubRepositoryApi { private final RpcCaller caller; /** @param caller the RPC transport function */ - ServerAccountsApi(RpcCaller caller) { + ServerGitHubRepositoryApi(RpcCaller caller) { this.caller = caller; } /** - * OneAuth token request supplied by a trusted host application. + * Resolves the GitHub repository that owns a working-tree path by reading the selected git remote configured for it, preferring `origin`. Returns a null `repository` when the path is inside a git working tree but that selected remote does not resolve to a GitHub host. Fails when the path is not inside a git working tree at all, so a caller can tell 'not a repository' apart from 'a repository with no GitHub remote'. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture acquireEntraToken(AccountsAcquireEntraTokenParams params) { - return caller.invoke("accounts.acquireEntraToken", params, EntraTokenAcquireResult.class); + CompletableFuture atPath(GitHubRepositoryAtPathParams params) { + return caller.invoke("gitHubRepository.atPath", params, GitHubRepositoryAtPathResult.class); } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGlobalStateApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGlobalStateApi.java new file mode 100644 index 0000000000..3c215395b8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerGlobalStateApi.java @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code globalState} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +final class ServerGlobalStateApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerGlobalStateApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * Reads the host's machine-wide state: which plugins are installed and the one-off flags and timestamps that record what the user has already been shown or migrated. This is the state that outlives a single session and a single workspace, so a host reads it to decide whether to run a first-launch step, offer an onboarding prompt, or skip one it has already completed. The stored credentials are deliberately not part of this result; a caller that needs an authenticated identity asks the account methods for it instead. Reading is non-destructive and every field is optional, because a fresh install has recorded nothing yet. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture load() { + return caller.invoke("globalState.load", java.util.Map.of(), GlobalStateLoadResult.class); + } + + /** + * Reads the host's machine-wide state exactly as `globalState.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 state read matches the directory that session actually uses. An absent or empty `configDir` resolves the server's own home, making this identical to `globalState.load`. The stored credentials are omitted here for the same reason they are omitted from `globalState.load`: a caller that needs an authenticated identity asks the account methods instead, so pointing this at another directory cannot be used to read the credentials kept in it. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture loadForConfigDir(GlobalStateLoadForConfigDirParams params) { + return caller.invoke("globalState.loadForConfigDir", params, GlobalStateLoadResult.class); + } + + /** + * Records one top-level key in the host's machine-wide state, the counterpart to `globalState.load`. A host calls this to remember that it has shown an onboarding step, asked a one-off question, or completed a migration, so the next run can skip it. Only the named key is replaced and the rest of the document is preserved, which lets two writers record different flags without overwriting each other; passing no value removes the key instead. Only the keys a host records itself are writable: `appInstallNudgeResponded`, `appTipShown`, `askedSetupTerminals`, `autoFeedbackLastPromptedAt`, `firstLaunchAt`, `recentModelIds`, `sandboxCredentialProxyCaDeclined` and `sandboxOnboardingShown`. Every other key is refused, including `installedPlugins`, the stored credentials, `trustedFolders`, the staff flags and the signed-in accounts. Plugin enablement must use the plugin APIs, which apply repository and managed-policy checks. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture writeKey(GlobalStateWriteKeyParams params) { + return caller.invoke("globalState.writeKey", params, Void.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 b6f69bf7dc..f41fbd63ac 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 @@ -27,7 +27,7 @@ public final class ServerHooksApi { } /** - * Optional project paths and host-exclusion behavior for server-scoped hook discovery. + * Discovers hook actions enabled under server-side discovery settings from user, repository, plugin, and managed-policy sources. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerHostApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerHostApi.java index cd2f56bda7..613fc1a318 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerHostApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerHostApi.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; @@ -27,7 +28,7 @@ public final class ServerHostApi { } /** - * Publishes a resident session attached to the listener's owning connection. + * Publishes an attached resident session for this listener's lifetime without copying it. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,18 +39,29 @@ public CompletableFuture publishSession(HostPublishSes } /** - * One application-owned session handoff, requested by the supervised hosting participant. + * Reads the complete advertised live and dormant session catalog of an existing host owned by this connection. Does not start hosting, subscribe to sessions, or guarantee resumability. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - CompletableFuture createSession(HostCreateSessionParams params) { - return caller.invoke("host.createSession", params, HostCreateSessionResult.class); + public CompletableFuture listSessions(HostListSessionsParams params) { + return caller.invoke("host.listSessions", params, HostListSessionsResult.class); } /** - * Ends one participation, not the application's session lifetime. + * Requests app-owned materialization over the owning SDK participant. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture createSession(HostCreateSessionParams params) { + return caller.invoke("host.createSession", params, HostSessionCreateResult.class); + } + + /** + * Releases app ownership retention after AHP detaches. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -60,7 +72,7 @@ CompletableFuture releaseSession(HostReleaseSessionParams params) { } /** - * Starts a supervised AHP host with at least one explicitly selected transport. + * Starts a connection-owned AHP host with explicit localServer and/or githubEnvironment transports as a supervised SDK participant. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -71,7 +83,20 @@ public CompletableFuture start(HostStartParams params) { } /** - * Stops a connection-owned listener and joins its teardown. + * Starts a connection-owned AHP host with explicit localServer and/or githubEnvironment transports as a supervised SDK participant. + *

+ * 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 start(HostStartRequest request) { + return caller.invoke("host.start", Objects.requireNonNull(request, "request"), HostStartResult.class); + } + + /** + * Stops a listener owned by this SDK connection and joins its cleanup without deleting sessions. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -82,29 +107,29 @@ public CompletableFuture dispose(HostDisposeParams params) { } /** - * Empty acknowledgement for a completed host lifecycle operation. + * Resolves current authenticated credentials and remote-control policy only for the runtime-owned Mission Control hosting participant. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - CompletableFuture getEnvironmentCredentials() { - return caller.invoke("host.getEnvironmentCredentials", java.util.Map.of(), HostGetEnvironmentCredentialsResult.class); + CompletableFuture getEnvironmentCredentials() { + return caller.invoke("host.getEnvironmentCredentials", java.util.Map.of(), HostEnvironmentCredentials.class); } /** - * Empty acknowledgement for a completed host lifecycle operation. + * Returns listener settings only to the supervised hosting participant over its SDK connection. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - CompletableFuture getConfiguration() { - return caller.invoke("host.getConfiguration", java.util.Map.of(), HostGetConfigurationResult.class); + CompletableFuture getConfiguration() { + return caller.invoke("host.getConfiguration", java.util.Map.of(), HostConfiguration.class); } /** - * Readiness reported by the supervised hosting participant on its own SDK connection. + * Reports a supervised hosting participant's bound AHP endpoint after its SDK handshake. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerInstructionsApi.java index 70eb5b0211..23d8c7e2de 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 @@ -27,7 +27,7 @@ public final class ServerInstructionsApi { } /** - * Optional project paths to include in instruction discovery. + * Discovers instruction sources across user, repository, and plugin sources. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture discover(InstructionsDiscov } /** - * Optional project paths to include when enumerating instruction discovery targets. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java index 4b7663f1f3..753eccb9f3 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerLlmInferenceApi.java @@ -27,7 +27,7 @@ public final class ServerLlmInferenceApi { } /** - * Indicates whether the calling client was registered as the LLM inference provider. + * Registers an SDK client as the LLM inference callback provider. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture setProvider() { } /** - * Response head. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,7 +49,7 @@ public CompletableFuture httpResponseStart( } /** - * A response body chunk or terminal error. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerManagedSettingsApi.java index 4f9cd612bf..a686df708c 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 @@ -27,7 +27,7 @@ public final class ServerManagedSettingsApi { } /** - * Validated device-managed settings discovered before a session exists. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture read() { } /** - * Invokes {@code managedSettings.clearCache}. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,7 +49,7 @@ public CompletableFuture clearCache() { } /** - * Optional opaque account selection or GitHub token whose managed settings are resolved. + * 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. *

* Invokes the method with no params, applying the runtime defaults. * @@ -62,7 +62,7 @@ public CompletableFuture resolve() { } /** - * Optional opaque account selection or GitHub token whose managed settings are resolved. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -73,7 +73,7 @@ public CompletableFuture resolve(ManagedSettingsRe } /** - * The authoring JSON schema for managed settings recognized by this runtime. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -84,7 +84,7 @@ public CompletableFuture schema() { } /** - * A candidate managed-settings document to validate without applying it. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -95,7 +95,7 @@ public CompletableFuture validate(ManagedSettings } /** - * Candidate managed-settings documents to merge without applying them. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpApi.java index 9557a2f4ac..553a644214 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 @@ -35,7 +35,7 @@ public final class ServerMcpApi { } /** - * Optional working directory used as context for MCP server discovery. + * Discovers MCP servers from user, workspace, plugin, and builtin sources. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -46,7 +46,7 @@ public CompletableFuture discover(McpDiscoverParams params) { } /** - * A side-effect-free request for an MCP install plan. Computing a plan never writes configuration, stores a secret, or reloads MCP servers. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -57,7 +57,7 @@ public CompletableFuture planInstall(McpPlanInstallParams } /** - * A side-effect-free request for an MCP install plan. Computing a plan never writes configuration, stores a secret, or reloads MCP servers. + * 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. *

* Accepts the extensible request, including inputs added after the params record. * @@ -70,7 +70,7 @@ public CompletableFuture planInstall(McpPlanInstallRequest } /** - * Side-effect-free preparation of one original bound remote MCP choice. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -81,7 +81,7 @@ public CompletableFuture prepareInstall(McpPrep } /** - * Applies exactly one previously prepared operation on its original connection. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -92,7 +92,7 @@ public CompletableFuture applyInstall(McpApplyInstallPara } /** - * Read-only preparation of one owned removal under fresh selected-session authority. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -103,7 +103,7 @@ public CompletableFuture planUninstall(McpPlanU } /** - * One-use application of the exact retained removal plan. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java index 6d3510f515..7271e94e29 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpConfigApi.java @@ -27,7 +27,7 @@ public final class ServerMcpConfigApi { } /** - * User-configured MCP servers, keyed by server name. + * Lists MCP servers from user configuration. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture list() { } /** - * MCP server name and configuration to add to user configuration. + * Adds an MCP server to user configuration. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,7 +49,7 @@ public CompletableFuture add(McpConfigAddParams params) { } /** - * MCP server name and replacement configuration to write to user configuration. + * Updates an MCP server in user configuration. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -60,7 +60,7 @@ public CompletableFuture update(McpConfigUpdateParams params) { } /** - * MCP server name to remove from user configuration. + * Removes an MCP server from user configuration. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -71,7 +71,7 @@ public CompletableFuture remove(McpConfigRemoveParams params) { } /** - * MCP server names to enable for new sessions. + * Enables MCP servers in user configuration for new sessions. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -82,7 +82,7 @@ public CompletableFuture enable(McpConfigEnableParams params) { } /** - * MCP server names to disable for new sessions. + * Disables MCP servers in user configuration for new sessions. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -93,7 +93,7 @@ public CompletableFuture disable(McpConfigDisableParams params) { } /** - * Invokes {@code mcp.config.reload}. + * Drops this runtime process's in-memory MCP server-definition cache so the next MCP config read observes disk. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpInstallationsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpInstallationsApi.java index ec99b766e9..6055d0fd3f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpInstallationsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerMcpInstallationsApi.java @@ -27,7 +27,7 @@ public final class ServerMcpInstallationsApi { } /** - * New-work inventory or recovery request under an explicitly selected existing session. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture list(McpInstallationsL } /** - * New-work inventory or recovery request under an explicitly selected existing session. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,7 +49,7 @@ public CompletableFuture recover(McpInstallatio } /** - * Existing-operation control. A new session selector is deliberately not accepted. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -60,7 +60,7 @@ public CompletableFuture status(McpInstallation } /** - * Existing-operation control. A new session selector is deliberately not accepted. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java index e4fd3cbc53..491340551d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerModelsApi.java @@ -27,7 +27,7 @@ public final class ServerModelsApi { } /** - * Optional opaque account selection or compatibility GitHub token used to list models. + * Lists Copilot models available to the authenticated user. *

* Invokes the method with no params, applying the runtime defaults. * @@ -40,7 +40,7 @@ public CompletableFuture list() { } /** - * Optional opaque account selection or compatibility GitHub token used to list models. + * Lists Copilot models available to the authenticated user. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -51,7 +51,7 @@ public CompletableFuture list(ModelsListParams params) { } /** - * The running runtime's complete catalog of well-known built-in model IDs, including supported models and additional IDs with built-in metadata. + * Returns the running runtime's complete catalog of well-known built-in model IDs without authentication or network access. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsApi.java index da5ac6c0e8..4fd1a839b0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsApi.java @@ -34,7 +34,7 @@ public final class ServerPluginsApi { } /** - * Plugins installed in user/global state. + * Lists plugins installed in user/global state. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -45,7 +45,7 @@ public CompletableFuture list() { } /** - * Plugin source and optional working directory for relative-path resolution. + * Installs a plugin from a marketplace, GitHub repo, URL, or local path. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -56,7 +56,7 @@ public CompletableFuture install(PluginsInstallParams para } /** - * Name (or spec) of the plugin to uninstall. + * Uninstalls an installed plugin. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -67,7 +67,7 @@ public CompletableFuture uninstall(PluginsUninstallParams params) { } /** - * Name (or spec) of the plugin to update. + * Updates an installed plugin to its latest published version. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -78,7 +78,7 @@ public CompletableFuture update(PluginsUpdateParams params) } /** - * Result of updating all installed plugins. + * Updates every installed plugin to its latest published version. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -89,7 +89,7 @@ public CompletableFuture updateAll() { } /** - * Plugin names (or specs) to enable, plus the optional working directory the repository-controlled guard is evaluated against. + * Enables installed plugins for new sessions. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -100,7 +100,7 @@ public CompletableFuture enable(PluginsEnableParams params) { } /** - * Plugin names (or specs) to disable, plus the optional working directory the repository-controlled guard is evaluated against. + * Disables installed plugins for new sessions. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsBuiltinApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsBuiltinApi.java index 0610fcb28e..b35a77a817 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsBuiltinApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsBuiltinApi.java @@ -27,7 +27,7 @@ public final class ServerPluginsBuiltinApi { } /** - * Trusted built-in plugin directories to use for this runtime process. + * Replaces this server's trusted built-in plugin directories while no sessions are active. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsMarketplacesApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsMarketplacesApi.java index e01bbeeec7..93adce4997 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsMarketplacesApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerPluginsMarketplacesApi.java @@ -27,7 +27,7 @@ public final class ServerPluginsMarketplacesApi { } /** - * All registered marketplaces, including built-in defaults. + * Lists all registered marketplaces (defaults + user-added). * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture list() { } /** - * Marketplace source and optional working directory for relative-path resolution. + * Registers a new marketplace from a source (owner/repo, URL, or local path). * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,7 +49,7 @@ public CompletableFuture add(PluginsMarketplacesAd } /** - * Name of the marketplace to remove and an optional force flag. + * 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`. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -60,7 +60,7 @@ public CompletableFuture remove(PluginsMarketpl } /** - * Name of the marketplace whose plugin catalog to fetch. + * Lists plugins advertised by a registered marketplace. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -71,7 +71,7 @@ public CompletableFuture browse(PluginsMarketpl } /** - * Optional marketplace name; omit to refresh all. + * Re-fetches one or all registered marketplace catalogs. *

* Invokes the method with no params, applying the runtime defaults. * @@ -84,7 +84,7 @@ public CompletableFuture refresh() { } /** - * Optional marketplace name; omit to refresh all. + * Re-fetches one or all registered marketplace catalogs. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRpc.java index b12cf617f2..99f65a5f89 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 @@ -55,10 +55,18 @@ public final class ServerRpc { 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. */ @@ -71,8 +79,8 @@ public final class ServerRpc { public final ServerSessionsApi sessions; /** API methods for the {@code agentRegistry} namespace. */ public final ServerAgentRegistryApi agentRegistry; - /** API methods for the {@code accounts} namespace. */ - public final ServerAccountsApi accounts; + /** API methods for the {@code connectors} namespace. */ + public final ServerConnectorsApi connectors; /** * Creates a new server RPC client. @@ -96,19 +104,23 @@ public ServerRpc(RpcCaller 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); this.sessions = new ServerSessionsApi(caller); this.agentRegistry = new ServerAgentRegistryApi(caller); - this.accounts = new ServerAccountsApi(caller); + this.connectors = new ServerConnectorsApi(caller); } /** - * Optional message to echo back to the caller. + * Checks server responsiveness and returns protocol information. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -119,18 +131,18 @@ public CompletableFuture ping(PingParams params) { } /** - * Connection-level opt-ins for the `server.connect` handshake. Transport authentication is consumed by the native protocol boundary before dispatch. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture connect(ConnectParams params) { + CompletableFuture connect(ConnectParams params) { return caller.invoke("connect", params, ConnectResult.class); } /** - * Invokes {@code registerExtensionLaunchProvider}. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java index e57db70946..d09f784d8d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerRuntimeApi.java @@ -27,7 +27,7 @@ public final class ServerRuntimeApi { } /** - * Invokes {@code runtime.shutdown}. + * Gracefully shuts down an SDK-owned runtime. The response is sent only after cleanup completes; callers may then terminate the owned runtime process. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxApi.java index 9c33cda35c..1484140f6a 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 @@ -21,13 +21,17 @@ public final class ServerSandboxApi { private final RpcCaller caller; + /** API methods for the {@code sandbox.proxyCa} sub-namespace. */ + public final ServerSandboxProxyCaApi proxyCa; + /** @param caller the RPC transport function */ ServerSandboxApi(RpcCaller caller) { this.caller = caller; + this.proxyCa = new ServerSandboxProxyCaApi(caller); } /** - * 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. + * Reports whether the host running this runtime can run the command sandbox, without starting a session or spawning a sandboxed command. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxyCaApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxyCaApi.java new file mode 100644 index 0000000000..0cc59738e9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSandboxProxyCaApi.java @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 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.proxyCa} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class ServerSandboxProxyCaApi { + + private final RpcCaller caller; + + /** @param caller the RPC transport function */ + ServerSandboxProxyCaApi(RpcCaller caller) { + this.caller = caller; + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture getStatus(SandboxProxyCaGetStatusParams params) { + return caller.invoke("sandbox.proxyCa.getStatus", params, SandboxProxyCaGetStatusResult.class); + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture create(SandboxProxyCaCreateParams params) { + return caller.invoke("sandbox.proxyCa.create", params, SandboxProxyCaCreateResult.class); + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture rotate(SandboxProxyCaRotateParams params) { + return caller.invoke("sandbox.proxyCa.rotate", params, SandboxProxyCaRotateResult.class); + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture trust(SandboxProxyCaTrustParams params) { + return caller.invoke("sandbox.proxyCa.trust", params, SandboxProxyCaTrustResult.class); + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture remove() { + return caller.invoke("sandbox.proxyCa.remove", java.util.Map.of(), SandboxProxyCaRemoveResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java index 7f17687818..ebdcac5e9e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSecretsApi.java @@ -27,7 +27,7 @@ public final class ServerSecretsApi { } /** - * Secret values to add to the redaction filter. + * Registers secret values for redaction in session logs and exports. The SDK calls this to inject dynamically generated secret values (e.g., OIDC tokens). * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java index 872702997a..ff48cdfb76 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionFsApi.java @@ -27,7 +27,7 @@ public final class ServerSessionFsApi { } /** - * 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. + * Registers an SDK client as the session filesystem provider. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionsApi.java index bc65f86fcb..fd963c0cbd 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSessionsApi.java @@ -28,7 +28,7 @@ public final class ServerSessionsApi { } /** - * Open a session by creating, resuming, attaching, connecting to a remote, or handing off. + * Creates or resumes a local session and returns the opened session ID. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -39,7 +39,7 @@ public CompletableFuture open(SessionsOpenParams params) { } /** - * Source session identifier to fork from, optional event-ID boundary, and optional friendly name for the new session. + * Creates a new session by forking persisted history from an existing session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -50,7 +50,7 @@ public CompletableFuture fork(SessionsForkParams params) { } /** - * Remote session connection parameters. + * Connects to an existing remote session and exposes it as an SDK session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -61,7 +61,7 @@ public CompletableFuture connect(SessionsConnectParams pa } /** - * Optional source filter, metadata-load limit, and context filter applied to the returned sessions. + * 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.). *

* Invokes the method with no params, applying the runtime defaults. * @@ -74,7 +74,7 @@ public CompletableFuture list() { } /** - * Optional source filter, metadata-load limit, and context filter applied to the returned sessions. + * 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.). * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -85,18 +85,18 @@ public CompletableFuture list(SessionsListParams params) { } /** - * Session ID whose persisted metadata should be read. + * Reads lightweight persisted metadata for one local session without opening it. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture getMetadata(SessionsGetMetadataParams params) { + CompletableFuture getMetadata(SessionsGetMetadataParams params) { return caller.invoke("sessions.getMetadata", params, SessionsGetMetadataResult.class); } /** - * Bounded batch request for client-owned metadata from persisted local sessions. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -107,7 +107,7 @@ public CompletableFuture> getClientMetadata(SessionsGetClientMetada } /** - * Pagination options for reading an inactive or active local session's persisted event journal. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -118,18 +118,18 @@ public CompletableFuture readPersistedEvents( } /** - * Limit for non-empty local session IDs. + * Lists recent local session IDs that contain user-visible history, omitting housekeeping-only sessions. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture listNonEmptySessionIds(SessionsListNonEmptySessionIdsParams params) { + CompletableFuture listNonEmptySessionIds(SessionsListNonEmptySessionIdsParams params) { return caller.invoke("sessions.listNonEmptySessionIds", params, SessionsListNonEmptySessionIdsResult.class); } /** - * GitHub task ID to look up. + * Finds the local session bound to a GitHub task ID, if any. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -140,7 +140,7 @@ public CompletableFuture findByTaskId(SessionsFindBy } /** - * UUID prefix to resolve to a unique session ID. + * Resolves a UUID prefix to a unique session ID, if exactly one session matches. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -151,7 +151,7 @@ public CompletableFuture findByPrefix(SessionsFindBy } /** - * Optional working-directory context used to score session relevance. + * Returns the most-relevant prior session for a given working-directory context. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -162,18 +162,18 @@ public CompletableFuture getLastForContext(Sess } /** - * Session ID whose event-log file path to compute. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture getEventFilePath(SessionsGetEventFilePathParams params) { + CompletableFuture getEventFilePath(SessionsGetEventFilePathParams params) { return caller.invoke("sessions.getEventFilePath", params, SessionsGetEventFilePathResult.class); } /** - * Map of sessionId -> on-disk size in bytes for each session's workspace directory. + * Returns the on-disk byte size of each session's workspace directory. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -184,7 +184,7 @@ public CompletableFuture getSizes() { } /** - * Session IDs to test for live in-use locks. + * Returns the subset of the supplied session IDs that are currently held by another running process. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -195,18 +195,18 @@ public CompletableFuture checkInUse(SessionsCheckInUse } /** - * Session ID to look up the persisted remote-steerable flag for. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture getPersistedRemoteSteerable(SessionsGetPersistedRemoteSteerableParams params) { + CompletableFuture getPersistedRemoteSteerable(SessionsGetPersistedRemoteSteerableParams params) { return caller.invoke("sessions.getPersistedRemoteSteerable", params, SessionsGetPersistedRemoteSteerableResult.class); } /** - * Session ID to close. + * Closes a session: emits shutdown, flushes pending events, releases the in-use lock, and disposes the active session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -217,7 +217,7 @@ public CompletableFuture close(SessionsCloseParams params) { } /** - * Session IDs to close, deactivate, and delete from disk. + * Closes, deactivates, and deletes a set of sessions, returning the bytes freed per session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -228,18 +228,18 @@ public CompletableFuture bulkDelete(SessionsBulkDelete } /** - * Session ID to delete from disk. + * Deletes one local session from disk after running the same lifecycle hooks as the session manager. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture delete(SessionsDeleteParams params) { + CompletableFuture delete(SessionsDeleteParams params) { return caller.invoke("sessions.delete", params, Void.class); } /** - * Age threshold and optional flags controlling which old sessions are pruned (or simulated when dryRun is true). + * Deletes sessions older than the given threshold, with optional dry-run and exclusion list. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -250,7 +250,7 @@ public CompletableFuture pruneOld(SessionsPruneOldParams } /** - * Session ID whose pending events should be flushed to disk. + * Flushes a session's pending events to disk. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -261,7 +261,7 @@ public CompletableFuture save(SessionsSaveParams params) { } /** - * Session ID whose in-use lock should be released. + * Releases the in-use lock held by this process for a session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -272,7 +272,7 @@ public CompletableFuture releaseLock(SessionsReleaseLockParams params) { } /** - * Session metadata records to enrich with summary and context information. + * Backfills missing summary and context fields on the supplied session metadata records. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -283,7 +283,40 @@ public CompletableFuture enrichMetadata(SessionsEn } /** - * Active session ID and an optional flag for deferring repo-level hooks until folder trust. + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture createWorkspace(SessionsCreateWorkspaceParams params) { + return caller.invoke("sessions.createWorkspace", params, SessionsCreateWorkspaceResult.class); + } + + /** + * 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. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture loadWorkspace(SessionsLoadWorkspaceParams params) { + return caller.invoke("sessions.loadWorkspace", params, SessionsLoadWorkspaceResult.class); + } + + /** + * 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`. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + CompletableFuture updateWorkspaceFields(SessionsUpdateWorkspaceFieldsParams params) { + return caller.invoke("sessions.updateWorkspaceFields", params, Void.class); + } + + /** + * Reloads user, plugin, and (optionally) repo hooks on the active session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -294,7 +327,7 @@ public CompletableFuture reloadPluginHooks(SessionsReloadPluginHooksParams } /** - * Active session ID whose deferred repo-level hooks should be loaded. + * Loads previously-deferred repo-level hooks on the active session, returning queued startup prompts. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -305,7 +338,7 @@ public CompletableFuture loadDeferredRepoHo } /** - * Manager-wide additional plugins to register; replaces any previously-configured set. + * Replaces the manager-wide additional plugins registered with the session manager. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -316,18 +349,18 @@ public CompletableFuture setAdditionalPlugins(SessionsSetAdditionalPlugins } /** - * Session ID whose board entry count should be returned. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture getBoardEntryCount(SessionsGetBoardEntryCountParams params) { + CompletableFuture getBoardEntryCount(SessionsGetBoardEntryCountParams params) { return caller.invoke("sessions.getBoardEntryCount", params, SessionsGetBoardEntryCountResult.class); } /** - * Parameters for attaching the remote-control singleton to a session. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -338,7 +371,7 @@ public CompletableFuture startRemoteControl(Se } /** - * Parameters for atomically rebinding the remote-control singleton. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -349,7 +382,7 @@ public CompletableFuture transferRemoteCont } /** - * Patch for the singleton's steering state. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -360,7 +393,7 @@ public CompletableFuture setRemoteContro } /** - * Parameters for stopping the remote-control singleton. + * 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). *

* Invokes the method with no params, applying the runtime defaults. * @@ -373,7 +406,7 @@ public CompletableFuture stopRemoteControl() { } /** - * Parameters for stopping the remote-control singleton. + * 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). * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -384,7 +417,7 @@ public CompletableFuture stopRemoteControl(Sess } /** - * Wrapper for the singleton's current status. + * Returns the current state of the remote-control singleton, including the attached session id and frontend URL when active. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -395,13 +428,13 @@ public CompletableFuture getRemoteControlS } /** - * Params to attach or detach an in-process ExtensionController delegate. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture configureSessionExtensions(SessionsConfigureSessionExtensionsParams params) { + CompletableFuture configureSessionExtensions(SessionsConfigureSessionExtensionsParams params) { return caller.invoke("sessions.configureSessionExtensions", params, Void.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 2f77e8da88..30b35346bb 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 @@ -34,7 +34,7 @@ public final class ServerSkillsApi { } /** - * Side-effect-free planning of one verified Agent Finder Skill candidate. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -45,7 +45,7 @@ public CompletableFuture planInstall(SkillsPl } /** - * Applies exactly one retained verified Skill installation plan. + * Consumes one verified Skill installation plan, requests explicit human consent through installations.confirm on the original connection, then revalidates and installs the Skill disabled. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -56,7 +56,7 @@ public CompletableFuture applyInstall(SkillsApplyInstal } /** - * Read-only preparation of one owned Skill removal under fresh selected-session authority. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -67,7 +67,7 @@ public CompletableFuture planUninstall(Skills } /** - * One-use application of the exact retained Skill removal plan. + * Consumes an owned Skill removal plan, requests explicit human consent through installations.confirm, refuses drift, and removes the exact owned files through quarantine. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -78,7 +78,7 @@ public CompletableFuture applyUninstall(SkillsApplyUnin } /** - * Optional project paths and additional skill directories to include in discovery. + * Discovers skills across global and project sources. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -89,7 +89,7 @@ public CompletableFuture discover(SkillsDiscoverParams par } /** - * Optional project paths to enumerate. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java index c29770bf14..35cf2e2ef1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsConfigApi.java @@ -27,7 +27,7 @@ public final class ServerSkillsConfigApi { } /** - * Skill names to mark as disabled in global configuration, replacing any previous list. + * Replaces the global list of disabled skills. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture setDisabledSkills(SkillsConfigSetDisabledSkillsPa } /** - * Adds or removes a single skill from the global disabled list, leaving every other entry untouched. + * Atomically adds or removes one skill from the disabled list. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsInstallationsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsInstallationsApi.java index e0d0afe72a..51029bc363 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsInstallationsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerSkillsInstallationsApi.java @@ -27,7 +27,7 @@ public final class ServerSkillsInstallationsApi { } /** - * Inventory request under an explicitly selected existing session. + * Lists owned verified Agent Finder Skill installations for the selected existing session. Listing is never gated by the Skill-install feature flag. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -38,7 +38,7 @@ public CompletableFuture list(SkillsInstallat } /** - * Inventory request under an explicitly selected existing session. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,7 +49,7 @@ public CompletableFuture recover(SkillsInstal } /** - * Existing-operation control. A new session selector is deliberately not accepted. + * Inspects a known Skill installation operation on its original runtime connection. Status is never gated by the Skill-install feature flag. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -60,7 +60,7 @@ public CompletableFuture status(SkillsInstall } /** - * Existing-operation control. A new session selector is deliberately not accepted. + * Requests cancellation of a known Skill installation operation before commit. Already-started durable work requires recovery instead of silent replay. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -71,7 +71,7 @@ public CompletableFuture cancel(SkillsInstall } /** - * Persisted enablement update for one owned Skill installation. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java index 2938010010..6f3b3b8759 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerToolsApi.java @@ -27,7 +27,7 @@ public final class ServerToolsApi { } /** - * Optional model identifier whose tool overrides should be applied to the listing. + * Lists built-in tools available for a model. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/ServerUserSettingsApi.java index 665cfb107a..9c5656ff14 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 @@ -27,18 +27,7 @@ public final class ServerUserSettingsApi { } /** - * Invokes {@code user.settings.reload}. - * - * @apiNote This method is experimental and may change in a future version. - * @since 1.0.0 - */ - @CopilotExperimental - public CompletableFuture reload() { - return caller.invoke("user.settings.reload", java.util.Map.of(), Void.class); - } - - /** - * Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. + * 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. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,14 +38,14 @@ public CompletableFuture get() { } /** - * 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. + * Writes one or more user settings to settings.json, replacing each provided top-level key. A key whose value is null is removed. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture set(UserSettingsSetParams params) { - return caller.invoke("user.settings.set", params, UserSettingsSetResult.class); + public CompletableFuture set(UserSettingsSetParams params) { + return caller.invoke("user.settings.set", params, Void.class); } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAccountsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAccountsApi.java index 76b3fdc7e0..2b4f752fdf 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAccountsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAccountsApi.java @@ -35,7 +35,7 @@ public final class SessionAccountsApi { } /** - * Enumerate request carrying the typed collection query. + * Enumerate a typed accounts collection: the signed-in accounts, or the providers offered for interactive login. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -51,7 +51,7 @@ public CompletableFuture enumerate(SessionAccountsEnumerateP } /** - * Read request carrying the typed datum query. + * Read one typed accounts datum: the active account, a neutral status summary, or the last authentication errors. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -67,7 +67,7 @@ public CompletableFuture get(SessionAccountsGetParams params) { } /** - * Mutation request carrying the typed write command. + * Apply one non-interactive accounts mutation: switch the active account, log an account out, or set credentials from a token. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAccountsLoginApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAccountsLoginApi.java index 5537a0dad2..24e3c6a642 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAccountsLoginApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAccountsLoginApi.java @@ -31,7 +31,7 @@ public final class SessionAccountsLoginApi { } /** - * Begin an interactive login flow for a provider kind. Dispatch is kind-only. + * Begin an interactive login flow for a provider kind (dispatch is kind-only) and return its opaque flow id and first step. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture begin(SessionAccountsL } /** - * Advance an in-flight login flow, optionally fulfilling an input-required step. + * Advance an in-flight login flow, optionally fulfilling an input-required step, and return the next step. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -63,7 +63,7 @@ public CompletableFuture advance(SessionAccountsLoginAdvanceParam } /** - * Cancel an in-flight login flow. + * Cancel an in-flight login flow and release its resources. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java index d2499fe3a5..0030c622d0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAgentApi.java @@ -31,7 +31,7 @@ public final class SessionAgentApi { } /** - * Controls whether built-in agents and authored prompt text are included. + * Lists agents available to the session. Defaults to custom agents only; pass includeBuiltInAgents to include the effective built-in agents. *

* Invokes the method with no params, applying the runtime defaults. * @@ -44,7 +44,7 @@ public CompletableFuture list() { } /** - * Controls whether built-in agents and authored prompt text are included. + * Lists agents available to the session. Defaults to custom agents only; pass includeBuiltInAgents to include the effective built-in agents. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -60,7 +60,7 @@ public CompletableFuture list(SessionAgentListParams par } /** - * An in-memory authored prompt override for an available agent. + * Sets an in-memory authored prompt override for an available agent. For built-in agents, this replaces only the static base prompt while preserving runtime-owned dynamic prompt composition and behavior. The special `general-purpose` agent is not overrideable. Overrides are not persisted; resumed and forked sessions start without them, so the host must re-apply them. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -76,7 +76,7 @@ public CompletableFuture setPrompt(SessionAgentSetPromptParams params) { } /** - * Identifies the target session. + * Gets the currently selected custom agent for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -87,7 +87,7 @@ public CompletableFuture getCurrent() { } /** - * Name of the custom agent to select for subsequent turns. + * Selects a custom agent for subsequent turns in the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -103,7 +103,7 @@ public CompletableFuture select(SessionAgentSelectPara } /** - * Identifies the target session. + * Clears the selected custom agent and returns the session to the default agent. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -114,7 +114,7 @@ public CompletableFuture deselect() { } /** - * Identifies the target session. + * Reloads custom agent definitions and returns the refreshed list. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAutopilotObjectiveApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAutopilotObjectiveApi.java index ab4058e6b6..8edc125d7e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAutopilotObjectiveApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionAutopilotObjectiveApi.java @@ -29,7 +29,7 @@ public final class SessionAutopilotObjectiveApi { } /** - * Identifies the target session. + * Reads the current canonical autopilot objective state for this session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionApi.java index 8c6ce4b79e..3e41100db6 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasActionApi.java @@ -31,7 +31,7 @@ public final class SessionCanvasActionApi { } /** - * Canvas action invocation parameters. + * Invokes an action on an open canvas instance. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasApi.java index e7cfc4be2f..fdcee24883 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasApi.java @@ -27,7 +27,7 @@ public final class SessionCanvasApi { /** API methods for the {@code canvas.action} sub-namespace. */ public final SessionCanvasActionApi action; /** API methods for the {@code canvas.provider} sub-namespace. */ - public final SessionCanvasProviderApi provider; + final SessionCanvasProviderApi provider; /** @param caller the RPC transport function */ SessionCanvasApi(RpcCaller caller, String sessionId) { @@ -38,7 +38,7 @@ public final class SessionCanvasApi { } /** - * Identifies the target session. + * Lists canvases declared for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -49,7 +49,7 @@ public CompletableFuture list() { } /** - * Identifies the target session. + * Lists currently open canvas instances for the live session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -60,7 +60,7 @@ public CompletableFuture listOpen() { } /** - * Canvas open parameters. + * Opens or focuses a canvas instance. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -76,7 +76,7 @@ public CompletableFuture open(SessionCanvasOpenParams p } /** - * Canvas close parameters. + * Closes an open canvas instance. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderApi.java index 95fc7ca660..90a80dfce0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderApi.java @@ -17,7 +17,7 @@ * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public final class SessionCanvasProviderApi { +final class SessionCanvasProviderApi { private static final com.fasterxml.jackson.databind.ObjectMapper MAPPER = RpcMapper.INSTANCE; @@ -31,7 +31,7 @@ public final class SessionCanvasProviderApi { } /** - * Internal canvas provider registration parameters. + * Registers an internal canvas provider connection and its contributions. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -40,14 +40,14 @@ public final class SessionCanvasProviderApi { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture register(SessionCanvasProviderRegisterParams params) { + CompletableFuture register(SessionCanvasProviderRegisterParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); return caller.invoke("session.canvas.provider.register", _p, Void.class); } /** - * Internal canvas provider unregistration parameters. + * Unregisters an internal canvas provider connection. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -56,7 +56,7 @@ public CompletableFuture register(SessionCanvasProviderRegisterParams para * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture unregister(SessionCanvasProviderUnregisterParams params) { + CompletableFuture unregister(SessionCanvasProviderUnregisterParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); return caller.invoke("session.canvas.provider.unregister", _p, Void.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 3047576670..a9213671aa 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 @@ -24,7 +24,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionCanvasProviderRegisterParams( +record SessionCanvasProviderRegisterParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Connection identifier for callback routing */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderUnregisterParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderUnregisterParams.java index f7b637ac65..4fc7273603 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderUnregisterParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCanvasProviderUnregisterParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionCanvasProviderUnregisterParams( +record SessionCanvasProviderUnregisterParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Connection identifier to unregister */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsApi.java index d2ef6a8f5c..55cd139ae5 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsApi.java @@ -31,7 +31,7 @@ public final class SessionCommandsApi { } /** - * Optional filters controlling which command sources to include in the listing. + * Lists slash commands available in the session. *

* Invokes the method with no params, applying the runtime defaults. * @@ -44,7 +44,7 @@ public CompletableFuture list() { } /** - * Optional filters controlling which command sources to include in the listing. + * Lists slash commands available in the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -60,7 +60,7 @@ public CompletableFuture list(SessionCommandsListPara } /** - * Slash command name and optional raw input string to invoke. + * Invokes a slash command in the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -76,7 +76,7 @@ public CompletableFuture invoke(SessionCommandsInv } /** - * The pending slash-command invocation effect to finalize, plus whether the host applied or cancelled it. + * Finalizes persistence associated with a client-applied slash-command effect. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -85,14 +85,14 @@ public CompletableFuture invoke(SessionCommandsInv * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture finalizeInvocationEffect(SessionCommandsFinalizeInvocationEffectParams params) { + CompletableFuture finalizeInvocationEffect(SessionCommandsFinalizeInvocationEffectParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.commands.finalizeInvocationEffect", _p, SessionCommandsFinalizeInvocationEffectResult.class); + return caller.invoke("session.commands.finalizeInvocationEffect", _p, CommandsFinalizeInvocationEffectResult.class); } /** - * Pending command request ID and an optional error if the client handler failed. + * Reports completion of a pending client-handled slash command. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -108,7 +108,7 @@ public CompletableFuture handlePendin } /** - * Slash command name and argument string to execute synchronously. + * Executes a slash command synchronously and returns any error. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -124,7 +124,7 @@ public CompletableFuture execute(SessionCommandsEx } /** - * Slash-prefixed command string to enqueue for FIFO processing. + * Enqueues a slash command for FIFO processing on the local session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -140,7 +140,7 @@ public CompletableFuture enqueue(SessionCommandsEnqueuePar } /** - * Queued-command request ID and the result indicating whether the host executed it (and whether to stop processing further queued commands). + * Reports whether the host actually executed a queued command and whether to continue processing. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsFinalizeInvocationEffectParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsFinalizeInvocationEffectParams.java index 2a1b75f5b8..337489704a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsFinalizeInvocationEffectParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCommandsFinalizeInvocationEffectParams.java @@ -24,7 +24,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionCommandsFinalizeInvocationEffectParams( +record SessionCommandsFinalizeInvocationEffectParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** The slash-command result object that produced the pending effect, echoed back unchanged. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsApi.java index 6b9d8aa252..f9fa5d1dae 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCompletionsApi.java @@ -31,7 +31,7 @@ public final class SessionCompletionsApi { } /** - * Identifies the target session. + * Gets the characters that should trigger host-driven completions for the session. Empty disables host-driven completions (e.g. local sessions, or a relay host that does not advertise them). * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture getTrigge } /** - * Request host-driven completions for the current composer input. + * Requests host-driven completion items for the current composer input. Returns an empty list when the host has no items or does not support completions. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectedIdeInfo.java similarity index 67% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectedIdeInfo.java index 7952fdc88b..9a2f0bce74 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectedIdeInfo.java @@ -14,19 +14,20 @@ import javax.annotation.processing.Generated; /** - * Result of registering or re-arming a scheduled prompt. + * The IDE a host is connected to, as reported to the session. + * + * @apiNote This type is experimental and may change in a future version. * - * @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 SessionScheduleAddAtResult( - /** The registered or updated schedule entry. */ - @JsonProperty("entry") ScheduleEntry entry, - /** User-facing validation error, when registration failed. */ - @JsonProperty("error") String error +record SessionConnectedIdeInfo( + /** Display name of the connected IDE, for example `VS Code`. */ + @JsonProperty("ideName") String ideName, + /** Absolute path of the workspace folder the IDE has open. */ + @JsonProperty("workspaceFolder") String workspaceFolder ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsApi.java index 775760f899..e69fc59d23 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsApi.java @@ -32,7 +32,7 @@ public final class SessionConnectorsApi { } /** - * Identifies the target session. + * Returns feature availability and bounded polling limits for the EXPERIMENTAL session connector API. This method never performs a Connector service request. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -43,7 +43,7 @@ public CompletableFuture getCapabilities } /** - * Identifies the target session. + * Returns the session account selection, or null. * * @return a future that completes with the {@code ConnectorSessionAccount} value, * or {@code null} when the result is absent. Callers must handle the @@ -58,7 +58,7 @@ public CompletableFuture getAccount() { } /** - * Identifies the target session. + * Returns authoritative session Connector state from current availability, pinned account selection, cached catalog, and live MCP projection without performing a Connector service request. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -69,7 +69,7 @@ public CompletableFuture getStatus() { } /** - * Pins a Connector operation to one host-owned GitHub account through its opaque selection ID. Provider tokens are never accepted. + * Returns the cached Connector catalog for the pinned opaque account selection, fetching it only when this session has no cached catalog. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -85,7 +85,7 @@ public CompletableFuture list(SessionConnectorsList } /** - * Pins a Connector operation to one host-owned GitHub account through its opaque selection ID. Provider tokens are never accepted. + * Refreshes and validates the Connector catalog for the pinned opaque account selection. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -101,7 +101,7 @@ public CompletableFuture refresh(SessionConnecto } /** - * Selects one Connector and the pinned host-owned account used for its service and MCP authorization. + * Initiates an idempotent Connector connection request without opening a browser. Returns connected when the service is immediately authoritative, consent_required with a validated URL, or pending with an opaque continuation ID. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -117,7 +117,7 @@ public CompletableFuture connect(SessionConnectorsConnec } /** - * Selects one Connector and the pinned host-owned account used for its service and MCP authorization. + * Re-initiates an idempotent Connector connection request without browser or UI effects, with the same typed outcomes as connect. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -133,7 +133,7 @@ public CompletableFuture reconnect(SessionConnectorsReco } /** - * Explicitly bounded continuation of a pending Connector connection. + * Continues a pending Connector connection with caller-supplied attempt, interval, and deadline bounds. The runtime never opens the returned consent URL. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -149,7 +149,7 @@ public CompletableFuture continueConnection(SessionConne } /** - * Selects one Connector and the pinned host-owned account used for its service and MCP authorization. + * Disconnects one Connector for the pinned opaque account selection, refreshes the authoritative catalog, and removes its session-owned MCP projection. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -165,7 +165,7 @@ public CompletableFuture disconnect(SessionCo } /** - * Requests authoritative Connector-to-MCP reconciliation for the pinned account. + * Reconciles the authoritative cached or freshly requested Connector catalog into the session Connector MCP projection and returns live status. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -181,7 +181,7 @@ public CompletableFuture reconcile(SessionConn } /** - * Requests authoritative Connector-to-MCP reconciliation for the pinned account. + * Reconciles the authoritative cached or freshly requested Connector catalog into the session Connector MCP projection and returns live status. *

* Accepts the extensible request, including inputs added after the params record. * @@ -196,7 +196,7 @@ public CompletableFuture reconcile(SessionConn } /** - * Pins a Connector operation to one host-owned GitHub account through its opaque selection ID. Provider tokens are never accepted. + * Reconciles the authoritative Connector catalog into the session MCP projection during startup with a bounded deadline and fail-closed cleanup. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -205,21 +205,21 @@ public CompletableFuture reconcile(SessionConn * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture reconcileForStartup(SessionConnectorsReconcileForStartupParams params) { + CompletableFuture reconcileForStartup(SessionConnectorsReconcileForStartupParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.connectors.reconcileForStartup", _p, SessionConnectorsReconcileForStartupResult.class); + return caller.invoke("session.connectors.reconcileForStartup", _p, ConnectorStatus.class); } /** - * Identifies the target session. + * Removes the runtime-owned Connector MCP projection without changing service-side connections. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture withdrawProjection() { - return caller.invoke("session.connectors.withdrawProjection", java.util.Map.of("sessionId", this.sessionId), SessionConnectorsWithdrawProjectionResult.class); + CompletableFuture withdrawProjection() { + return caller.invoke("session.connectors.withdrawProjection", java.util.Map.of("sessionId", this.sessionId), ConnectorStatus.class); } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsReconcileForStartupParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsReconcileForStartupParams.java index 194ee97ecf..3cd95a487c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsReconcileForStartupParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsReconcileForStartupParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionConnectorsReconcileForStartupParams( +record SessionConnectorsReconcileForStartupParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Opaque account selection ID previously returned by an account discovery API. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsReconcileForStartupResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsReconcileForStartupResult.java deleted file mode 100644 index fa563d54d5..0000000000 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsReconcileForStartupResult.java +++ /dev/null @@ -1,43 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All 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; - -/** - * Authoritative session connector state. Account IDs are opaque routing identifiers and credentials are never included. - * - * @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 SessionConnectorsReconcileForStartupResult( - /** Connector API contract version. */ - @JsonProperty("apiVersion") Long apiVersion, - /** Current feature and session availability. */ - @JsonProperty("availability") ConnectorAvailability availability, - /** Opaque account selection pinned to this session, when one has been selected. */ - @JsonProperty("accountId") String accountId, - /** Latest validated catalog snapshot, when available. */ - @JsonProperty("catalog") ConnectorCatalogResult catalog, - /** Exact selected account and stable scope requiring an authorization update, when proven by the Connector service. */ - @JsonProperty("authorizationRequirement") ConnectorAuthorizationRequirement authorizationRequirement, - /** Live MCP status for every Connector-owned runtime server. */ - @JsonProperty("runtimeServers") List runtimeServers, - /** Number of active opaque connection continuations. */ - @JsonProperty("pendingConnections") Long pendingConnections -) { -} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsWithdrawProjectionParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsWithdrawProjectionParams.java index 2e534c1979..66461d320f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsWithdrawProjectionParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsWithdrawProjectionParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionConnectorsWithdrawProjectionParams( +record SessionConnectorsWithdrawProjectionParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsWithdrawProjectionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsWithdrawProjectionResult.java deleted file mode 100644 index 1ae332cab0..0000000000 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionConnectorsWithdrawProjectionResult.java +++ /dev/null @@ -1,43 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All 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; - -/** - * Authoritative session connector state. Account IDs are opaque routing identifiers and credentials are never included. - * - * @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 SessionConnectorsWithdrawProjectionResult( - /** Connector API contract version. */ - @JsonProperty("apiVersion") Long apiVersion, - /** Current feature and session availability. */ - @JsonProperty("availability") ConnectorAvailability availability, - /** Opaque account selection pinned to this session, when one has been selected. */ - @JsonProperty("accountId") String accountId, - /** Latest validated catalog snapshot, when available. */ - @JsonProperty("catalog") ConnectorCatalogResult catalog, - /** Exact selected account and stable scope requiring an authorization update, when proven by the Connector service. */ - @JsonProperty("authorizationRequirement") ConnectorAuthorizationRequirement authorizationRequirement, - /** Live MCP status for every Connector-owned runtime server. */ - @JsonProperty("runtimeServers") List runtimeServers, - /** Number of active opaque connection continuations. */ - @JsonProperty("pendingConnections") Long pendingConnections -) { -} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionApi.java index eb621e6b49..afddaed971 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionContentExclusionApi.java @@ -31,7 +31,7 @@ public final class SessionContentExclusionApi { } /** - * Local file system absolute paths within the session working directory to check against its content-exclusion policy. + * Checks local file system absolute paths within the session working directory against its content-exclusion policy. Results preserve input order. Unsupported paths/filesystems and unavailable policy evaluation return available false, and callers must treat every requested path as excluded. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCustomizationsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCustomizationsApi.java index 18d27db9b5..b0183065b0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCustomizationsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionCustomizationsApi.java @@ -29,14 +29,14 @@ public final class SessionCustomizationsApi { } /** - * Identifies the target session. + * For local sessions, reconciles repository context and discovered instructions, plugins, skills, agents, hooks, MCP servers, and extensions after files appear or change under the working directory. Independent component failures are returned in outcomes and errors; a rejected call can have partially applied earlier steps. Remote sessions must reload on their agent host instead. The model-facing context is rebuilt on the next turn. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture reload() { - return caller.invoke("session.customizations.reload", java.util.Map.of("sessionId", this.sessionId), SessionCustomizationsReloadResult.class); + public CompletableFuture reload() { + return caller.invoke("session.customizations.reload", java.util.Map.of("sessionId", this.sessionId), CustomizationsReloadResult.class); } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugApi.java index b2cd030183..ed06730c49 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDebugApi.java @@ -31,7 +31,7 @@ public final class SessionDebugApi { } /** - * Options for collecting a session debug bundle with configurable redaction. + * Collects a session debug log bundle into a local archive or staging directory. Logs are redacted by default; redaction can be configured per caller-provided diagnostic entry. The runtime includes session-owned logs by default and accepts caller-provided diagnostic entries so host applications can add their own files without changing this API shape. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDiagnosticsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDiagnosticsApi.java index af4c57837b..d263853aec 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDiagnosticsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionDiagnosticsApi.java @@ -31,7 +31,7 @@ public final class SessionDiagnosticsApi { } /** - * Patch session diagnostic thresholds for explicitly supplied sources. + * Patches configured session diagnostic sources without restarting their producers. Setting a source level to off clears its retained diagnostics and invalidates cursors selecting that source. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture configure(SessionDia } /** - * Cursor-based request for session diagnostics. The default limit is 100 (maximum 500); the default waitMs is zero (maximum 30000). + * Reads a bounded batch of retained session diagnostics for the selected sources. Records are never consumed and each reader advances independently through its opaque cursor. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogApi.java index 8ad9b3b1f5..87ad200719 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionEventLogApi.java @@ -31,7 +31,7 @@ public final class SessionEventLogApi { } /** - * Cursor, batch size, and optional long-poll/filter parameters for reading session events. + * Reads a batch of session events from a cursor, optionally waiting for new events. Supports tail-first reads via `direction: backward`. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture read(SessionEventLogReadPara } /** - * Identifies the target session. + * Returns a snapshot of the current tail cursor without consuming events. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -58,7 +58,7 @@ public CompletableFuture tail() { } /** - * Event type to register consumer interest for, used by runtime gating logic. + * Registers consumer interest in an event type for runtime gating purposes. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -74,7 +74,7 @@ public CompletableFuture registerInterest } /** - * Opaque handle previously returned by `registerInterest` to release. + * Releases a consumer's previously-registered interest in an event type. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsApi.java index e21c109684..d4d5837dcc 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsApi.java @@ -31,7 +31,7 @@ public final class SessionExtensionsApi { } /** - * Identifies the target session. + * Lists extensions discovered for the session and their current status. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture list() { } /** - * Source-qualified extension identifier to enable for the session. + * Enables an extension for the session and persists the preference when the session has a settings store. Hosts synchronizing effective membership should use extensions.reconcile instead. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -58,7 +58,7 @@ public CompletableFuture enable(SessionExtensionsEnableParams params) { } /** - * Source-qualified extension identifier to disable for the session. + * Disables an extension for the session and persists the preference when the session has a settings store. Hosts synchronizing effective membership should use extensions.reconcile instead. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -74,7 +74,7 @@ public CompletableFuture disable(SessionExtensionsDisableParams params) { } /** - * Identifies the target session. + * Reloads extension definitions and processes for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -85,7 +85,18 @@ public CompletableFuture reload() { } /** - * Parameters for session.extensions.sendAttachmentsToMessage. + * Host-only reconciliation of authoritative session-effective extension membership and enablement. Refreshes runtime-owned discovery and preferences without persisting settings, installing plugins, or restarting unchanged activations. Returns ExtensionList only after required starts and process/contribution cleanup settle. Takes no caller inventory or overrides. Missing controllers, unready/incomplete discovery, unavailable workspaces, superseded inputs, and lifecycle failures are errors, not empty membership. Independently proven revocations may be applied before an error; retry converges without restarting healthy activations. Error data contains lifecycleChangesApplied and code: extension_reconciliation_host_required, extension_reconciliation_unavailable, extension_reconciliation_not_ready, extension_reconciliation_discovery_failed, extension_reconciliation_workspace_unavailable, extension_reconciliation_superseded, or extension_reconciliation_lifecycle_failed. Mark host reconciliation state applied only on success. On older runtimes, method-not-found must not fall back to global discovery and persistent extension disables. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture reconcile() { + return caller.invoke("session.extensions.reconcile", java.util.Map.of("sessionId", this.sessionId), SessionExtensionsReconcileResult.class); + } + + /** + * Push attachments into the next user-message turn from an extension. The host should surface them as composer pills and forward them via the next session.send call. Callable only by extension-owned connections. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReconcileParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReconcileParams.java new file mode 100644 index 0000000000..5e6ac8ef1b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReconcileParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Identifies the target session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionExtensionsReconcileParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReconcileResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReconcileResult.java new file mode 100644 index 0000000000..7d1472c947 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionExtensionsReconcileResult.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; + +/** + * Extensions discovered for the session, with their current status. + * + * @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 SessionExtensionsReconcileResult( + /** Discovered extensions and their current status */ + @JsonProperty("extensions") List extensions +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetApi.java index ca855b70a9..1c5730cd88 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFleetApi.java @@ -31,7 +31,7 @@ public final class SessionFleetApi { } /** - * Parameters for starting fleet orchestration: an optional user prompt combined with the fleet instructions, plus the send options forwarded to the resulting turn. + * Starts fleet mode by submitting the fleet orchestration prompt to the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsError.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsError.java index 349114dfde..0170b7f3b0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsError.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsError.java @@ -24,6 +24,8 @@ public record SessionFsError( /** Error classification */ @JsonProperty("code") SessionFsErrorCode code, /** Free-form detail about the error, for logging/diagnostics */ - @JsonProperty("message") String message + @JsonProperty("message") String message, + /** For failed writeFile requests only: true if the provider changed the target before failing. Omit when unknown or unchanged. */ + @JsonProperty("writeChanged") Boolean writeChanged ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostRegisterSessionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileBytesParams.java similarity index 77% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostRegisterSessionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileBytesParams.java index 111c45a1db..9e89b0c1a2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostRegisterSessionResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileBytesParams.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * The existing runtime identity and its resource on the listener. + * Path of the binary file to read from the client-provided session filesystem. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -23,10 +23,10 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostRegisterSessionResult( - /** Canonical runtime ID of the published session. */ +public record SessionFsReadFileBytesParams( + /** Target session identifier */ @JsonProperty("sessionId") String sessionId, - /** AHP resource URI for the session on this listener. */ - @JsonProperty("sessionUri") String sessionUri + /** Path using SessionFs conventions */ + @JsonProperty("path") String path ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileBytesResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileBytesResult.java new file mode 100644 index 0000000000..f70a78c72f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsReadFileBytesResult.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; + +/** + * File bytes as standard base64, or a filesystem error if the read failed. + * + * @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 SessionFsReadFileBytesResult( + /** Exact file bytes encoded as standard base64 */ + @JsonProperty("content") String content, + /** Describes a filesystem error. */ + @JsonProperty("error") SessionFsError error +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderCapabilities.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderCapabilities.java index 7d6c0adb39..f7d047c273 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderCapabilities.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsSetProviderCapabilities.java @@ -22,6 +22,19 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record SessionFsSetProviderCapabilities( /** Whether the provider supports SQLite query/exists operations */ - @JsonProperty("sqlite") Boolean sqlite + @JsonProperty("sqlite") Boolean sqlite, + /** Whether the provider supports binary reads and writes through sessionFs.readFileBytes and sessionFs.writeFileBytes */ + @JsonProperty("binary") Boolean binary ) { + + /** + * Creates provider capabilities without binary reads. + * + * @param sqlite Whether the provider supports SQLite query/exists operations + */ + public SessionFsSetProviderCapabilities( + Boolean sqlite + ) { + this(sqlite, null); + } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsWriteFileBytesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsWriteFileBytesParams.java new file mode 100644 index 0000000000..2d095f371d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionFsWriteFileBytesParams.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; + +/** + * File path, standard-base64-encoded bytes to write, and optional mode for the client-provided session filesystem. + * + * @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 SessionFsWriteFileBytesParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Path using SessionFs conventions */ + @JsonProperty("path") String path, + /** Exact file bytes encoded as standard base64 */ + @JsonProperty("content") String content, + /** Optional POSIX-style mode for newly created files */ + @JsonProperty("mode") Long mode +) { +} 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 4792927b9a..388576154a 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 @@ -32,7 +32,7 @@ public final class SessionGitHubAuthApi { } /** - * Identifies the target session. + * Gets authentication status and account metadata for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -43,7 +43,7 @@ public CompletableFuture getStatus() { } /** - * New auth credentials to install on the session. Omit to leave credentials unchanged. + * Updates the session's auth credentials used for outbound model and API requests. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -59,7 +59,7 @@ public CompletableFuture setCredentials(S } /** - * Identifies the target session. + * Gets the current authentication information for internal session hosts. * * @return a future that completes with the {@code AuthIdentity} value, * or {@code null} when the result is absent. Callers must handle the @@ -69,23 +69,23 @@ public CompletableFuture setCredentials(S * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture getCurrentAuthInfo() { + CompletableFuture getCurrentAuthInfo() { return caller.invoke("session.gitHubAuth.getCurrentAuthInfo", java.util.Map.of("sessionId", this.sessionId), AuthIdentity.class); } /** - * Identifies the target session. + * Gets all authentication accounts available to the internal session host. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture> getAllAuthAvailable() { + CompletableFuture> getAllAuthAvailable() { return caller.invoke("session.gitHubAuth.getAllAuthAvailable", java.util.Map.of("sessionId", this.sessionId), RpcMapper.INSTANCE.getTypeFactory().constructCollectionType(List.class, SessionAuthStatus.class)); } /** - * Identifies the target session. + * Refreshes Copilot account metadata for the current authentication. * * @return a future that completes with the {@code AuthIdentity} value, * or {@code null} when the result is absent. Callers must handle the @@ -95,12 +95,12 @@ public CompletableFuture> getAllAuthAvailable() { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture refreshCopilotUser() { + CompletableFuture refreshCopilotUser() { return caller.invoke("session.gitHubAuth.refreshCopilotUser", java.util.Map.of("sessionId", this.sessionId), AuthIdentity.class); } /** - * Internal GitHub login parameters. + * Logs in a GitHub user through the internal session host. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -109,14 +109,14 @@ public CompletableFuture refreshCopilotUser() { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture login(SessionGitHubAuthLoginParams params) { + CompletableFuture login(SessionGitHubAuthLoginParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); return caller.invoke("session.gitHubAuth.login", _p, AuthInfo.class); } /** - * Parameters for switching the session's active authentication. + * Switches the session to another available authentication. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -125,25 +125,25 @@ public CompletableFuture login(SessionGitHubAuthLoginParams params) { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture switchToAuth(SessionGitHubAuthSwitchToAuthParams params) { + CompletableFuture switchToAuth(SessionGitHubAuthSwitchToAuthParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); return caller.invoke("session.gitHubAuth.switchToAuth", _p, Void.class); } /** - * Identifies the target session. + * Logs out the session's current GitHub authentication. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture logout() { + CompletableFuture logout() { return caller.invoke("session.gitHubAuth.logout", java.util.Map.of("sessionId", this.sessionId), Void.class); } /** - * Parameters identifying a GitHub authentication to log out. + * Logs out a specific GitHub authentication. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -152,20 +152,20 @@ public CompletableFuture logout() { * @since 1.0.0 */ @CopilotExperimental - public 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); } /** - * Identifies the target session. + * Gets validation errors from the most recent authentication attempt. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture> lastAuthErrors() { + CompletableFuture> lastAuthErrors() { return caller.invoke("session.gitHubAuth.lastAuthErrors", java.util.Map.of("sessionId", this.sessionId), RpcMapper.INSTANCE.getTypeFactory().constructCollectionType(List.class, AuthValidationError.class)); } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetAllAuthAvailableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetAllAuthAvailableParams.java index 446642d743..8ebe3b7519 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetAllAuthAvailableParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetAllAuthAvailableParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionGitHubAuthGetAllAuthAvailableParams( +record SessionGitHubAuthGetAllAuthAvailableParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetCurrentAuthInfoParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetCurrentAuthInfoParams.java index 88838f951d..24eb44e32c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetCurrentAuthInfoParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthGetCurrentAuthInfoParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionGitHubAuthGetCurrentAuthInfoParams( +record SessionGitHubAuthGetCurrentAuthInfoParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLastAuthErrorsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLastAuthErrorsParams.java index d4fc156b28..02e7cec4f8 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLastAuthErrorsParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLastAuthErrorsParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionGitHubAuthLastAuthErrorsParams( +record SessionGitHubAuthLastAuthErrorsParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLoginParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLoginParams.java index e0d777956f..6fde66966b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLoginParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLoginParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionGitHubAuthLoginParams( +record SessionGitHubAuthLoginParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** GitHub host URL */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLogoutParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLogoutParams.java index 172778d616..07283331b5 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLogoutParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLogoutParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionGitHubAuthLogoutParams( +record SessionGitHubAuthLogoutParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLogoutUserParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLogoutUserParams.java index 1aafec7811..c59fff894b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLogoutUserParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthLogoutUserParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionGitHubAuthLogoutUserParams( +record SessionGitHubAuthLogoutUserParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Authentication information to log out */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthRefreshCopilotUserParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthRefreshCopilotUserParams.java index d0b9d33e56..3b0ee5f0a0 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthRefreshCopilotUserParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthRefreshCopilotUserParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionGitHubAuthRefreshCopilotUserParams( +record SessionGitHubAuthRefreshCopilotUserParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSwitchToAuthParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSwitchToAuthParams.java index 7cd6f72b41..db357cab5c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSwitchToAuthParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionGitHubAuthSwitchToAuthParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionGitHubAuthSwitchToAuthParams( +record SessionGitHubAuthSwitchToAuthParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Authentication information to activate */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java index ad44d864de..ccfbfbd17f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionHistoryApi.java @@ -31,7 +31,7 @@ public final class SessionHistoryApi { } /** - * Optional compaction parameters. + * Compacts the session history to reduce context usage. *

* Invokes the method with no params, applying the runtime defaults. * @@ -44,7 +44,7 @@ public CompletableFuture compact() { } /** - * Optional compaction parameters. + * Compacts the session history to reduce context usage. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -60,7 +60,7 @@ public CompletableFuture compact(SessionHistoryComp } /** - * Identifier of the event to truncate to; this event and all later events are removed. + * Truncates persisted session history to a specific event. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -76,7 +76,7 @@ public CompletableFuture truncate(SessionHistoryTr } /** - * Identifies the target session. + * Lists the user turns that the session can rewind to. Never rejects for a busy session: rewind reads need the session's file-change captures to be settled, so a session that still holds active work answers with `unavailableReason: "session-busy"` and no points, which the caller can retry. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -87,7 +87,7 @@ public CompletableFuture listRewindPoints( } /** - * Event boundary to preview for conversation-and-files rewind. + * Previews the files that a conversation-and-files rewind would restore. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -103,7 +103,7 @@ public CompletableFuture previewRewind(Sessio } /** - * Boundary and mode for rewinding session history. + * Rewinds the session conversation, optionally restoring files changed by the discarded turns. Not crash-atomic: file restore and conversation truncation are separate stores, applied in that order, so a process crash between them can leave the workspace rewound while the conversation still contains the discarded turns. There is no recovery journal; re-running the same rewind is the recovery path for a crash before truncation lands, since file restore is idempotent (already-restored files are reported as skipped) and truncation is re-derived from the still-retained boundary event. After truncation lands that boundary no longer exists, so the same request is rejected; the only stage that can still be outstanding is snapshot pruning, whose failure leaves orphan snapshots the capture store tolerates. The reverse inconsistency cannot occur, because truncation is never applied before file restore succeeds. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -119,7 +119,7 @@ public CompletableFuture rewind(SessionHistoryRewind } /** - * Identifies the target session. + * Cancels any in-progress background compaction on a local session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -130,7 +130,7 @@ public CompletableFuture cancelB } /** - * Identifies the target session. + * Aborts any in-progress manual compaction on a local session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -141,7 +141,7 @@ public CompletableFuture abortManualC } /** - * Identifies the target session. + * Produces a markdown summary of the session's conversation context for hand-off scenarios. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -152,7 +152,7 @@ public CompletableFuture summarizeForHa } /** - * Parameters for clearing the conversation and seeding the window that replaces it. + * Clears the session's conversation history, keeping only system and developer messages, and seeds the fresh context window with a first user message. Must be called from inside a tool handler: the clear has to drop the results of the tool calls its wipe orphans, and it rejects when no tool call is in flight. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsApi.java index d5971f70fe..1252fa8f59 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionInstructionsApi.java @@ -29,7 +29,7 @@ public final class SessionInstructionsApi { } /** - * Identifies the target session. + * Gets instruction sources loaded for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -40,14 +40,14 @@ public CompletableFuture getSources() { } /** - * Identifies the target session. + * For local sessions, invalidates instruction discovery and the model-facing prompt, then returns freshly discovered sources. The updated prompt takes effect on the next turn. Remote sessions must reload on their agent host instead. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture reload() { - return caller.invoke("session.instructions.reload", java.util.Map.of("sessionId", this.sessionId), Void.class); + public CompletableFuture reload() { + return caller.invoke("session.instructions.reload", java.util.Map.of("sessionId", this.sessionId), SessionInstructionsGetSourcesResult.class); } } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionApi.java index a64f82afae..b28b54bd37 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLimitPredictionApi.java @@ -31,7 +31,7 @@ public final class SessionLimitPredictionApi { } /** - * Parameters for predicting an AI-credit session limit. Omitting `modelId` uses the session's currently selected model. + * Predicts an AI-credit session limit for the session's resolved model. Returns an unavailable result instead of falling back when the current model is unresolved auto. *

* Invokes the method with no params, applying the runtime defaults. * @@ -44,7 +44,7 @@ public CompletableFuture predict() { } /** - * Parameters for predicting an AI-credit session limit. Omitting `modelId` uses the session's currently selected model. + * Predicts an AI-credit session limit for the session's resolved model. Returns an unavailable result instead of falling back when the current model is unresolved auto. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLspApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLspApi.java index c965007d43..2b20bd44e3 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLspApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionLspApi.java @@ -31,7 +31,7 @@ public final class SessionLspApi { } /** - * Parameters for (re)loading the merged LSP configuration set. + * Loads the merged LSP configuration set for the session's working directory. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java index 6bf568c985..c8193260d1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedPermissions.java @@ -24,11 +24,15 @@ public record SessionManagedPermissions( /** When set to `disable`, prevents bypass/allow-all permission modes. Advisory auto-approval remains available because normal prompt paths stay active. Any other value is accepted rather than failing the session, but is enforced as `disable`: the key is only present to restrict something, so a mode this runtime cannot interpret fails closed to the most restrictive one it knows. Omit the key entirely to impose no restriction. */ @JsonProperty("disableBypassPermissionsMode") String disableBypassPermissionsMode, + /** When true, prevents Assisted Permissions from being activated. An actively Assisted session falls back to Manual Approval while the policy is in force. Omit the key or set it to false to impose no restriction. */ + @JsonProperty("disableAssistedPermissionsMode") Boolean disableAssistedPermissionsMode, /** Permission rules that block matching operations. Deny has highest precedence. */ @JsonProperty("deny") List deny, /** Permission rules that require explicit human approval. */ @JsonProperty("ask") List ask, /** Permission rules that allow matching operations unless another managed source, deny, or ask rule restricts them. */ - @JsonProperty("allow") List allow + @JsonProperty("allow") List allow, + /** Closed-world host boundary expressed as `Domain(hostname)`, `Domain(IP)`, or `Domain(*.example.com)` rules. Schemes, ports, paths, queries, and fragments are rejected because every network request must be enforceable at host-level egress. Multiple managed sources intersect their lists; an empty list denies all hosts. */ + @JsonProperty("limitTo") List limitTo ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettingsApi.java index adf8568aa2..e2e4a67fe3 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettingsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionManagedSettingsApi.java @@ -29,7 +29,7 @@ public final class SessionManagedSettingsApi { } /** - * Identifies the target session. + * Waits for the live session's in-flight managed-settings application, then returns the retained effective snapshot used by runtime enforcement and by `session.managed_settings_resolved`. It does not perform another account, device, or server resolution, and rejects when resolution has not produced a snapshot. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpApi.java index 4cb7a08312..5f805354c4 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpApi.java @@ -33,6 +33,8 @@ public final class SessionMcpApi { public final SessionMcpAppsApi apps; /** API methods for the {@code mcp.resources} sub-namespace. */ public final SessionMcpResourcesApi resources; + /** API methods for the {@code mcp.prompts} sub-namespace. */ + public final SessionMcpPromptsApi prompts; /** @param caller the RPC transport function */ SessionMcpApi(RpcCaller caller, String sessionId) { @@ -42,10 +44,27 @@ public final class SessionMcpApi { this.headers = new SessionMcpHeadersApi(caller, sessionId); this.apps = new SessionMcpAppsApi(caller, sessionId); this.resources = new SessionMcpResourcesApi(caller, sessionId); + this.prompts = new SessionMcpPromptsApi(caller, sessionId); } /** - * Identifies the target session. + * Records the IDE the host is connected to, so the agent's system prompt can name it and its workspace folder. Null or an omitted `ide` clears the recorded value, which is how a host reports that it is disconnected; there is no separate clear method. Both `ideName` and `workspaceFolder` are required together, because half a state cannot be attributed to a project. + *

+ * 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 + CompletableFuture setConnectedIdeInfo(SessionMcpSetConnectedIdeInfoParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.mcp.setConnectedIdeInfo", _p, Void.class); + } + + /** + * Lists materialized MCP servers and their connection status. Cache misses may start and wait for MCP servers. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -56,7 +75,18 @@ public CompletableFuture list() { } /** - * Server name whose tool list should be returned. + * Lists effective MCP configuration without starting, restarting, authenticating, or waiting for servers. An optional live observation is from an already materialized matching server; this is not a readiness guarantee. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture listConfigured() { + return caller.invoke("session.mcp.listConfigured", java.util.Map.of("sessionId", this.sessionId), SessionMcpListConfiguredResult.class); + } + + /** + * Lists the tools exposed by a connected MCP server on this session's host. This performs a live `tools/list` request. Tool UI metadata is returned independently of whether MCP Apps rendering is enabled for the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -72,7 +102,7 @@ public CompletableFuture listTools(SessionMcpListTool } /** - * Name of the MCP server to enable for the session. + * Enables an MCP server for the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -88,7 +118,7 @@ public CompletableFuture enable(SessionMcpEnableParams params) { } /** - * Name of the MCP server to enable for the session. + * Enables an MCP server for the session. *

* Accepts the extensible request, including inputs added after the params record. * @@ -103,7 +133,7 @@ public CompletableFuture enable(SessionMcpEnableRequest request) { } /** - * Name of the MCP server to disable for the session. + * Disables an MCP server for the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -119,7 +149,7 @@ public CompletableFuture disable(SessionMcpDisableParams params) { } /** - * Name of the MCP server to disable for the session. + * Disables an MCP server for the session. *

* Accepts the extensible request, including inputs added after the params record. * @@ -134,7 +164,7 @@ public CompletableFuture disable(SessionMcpDisableRequest request) { } /** - * Identifies the target session. + * Reloads MCP server connections for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -145,7 +175,7 @@ public CompletableFuture reload() { } /** - * Identifies the target session. + * Releases any turns waiting on an in-flight MCP load without cancelling the load, letting the agent proceed while MCP servers finish connecting in the background. No-op when no MCP load is in flight or waiting turns were already released. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -156,7 +186,7 @@ public CompletableFuture moveLoadingToB } /** - * Opaque MCP reload configuration. + * Reloads MCP server connections for the session with an explicit host-provided configuration. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -165,14 +195,14 @@ public CompletableFuture moveLoadingToB * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture reloadWithConfig(SessionMcpReloadWithConfigParams params) { + CompletableFuture reloadWithConfig(SessionMcpReloadWithConfigParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.mcp.reloadWithConfig", _p, SessionMcpReloadWithConfigResult.class); + return caller.invoke("session.mcp.reloadWithConfig", _p, McpStartServersResult.class); } /** - * Identifiers and raw MCP CreateMessageRequest params used to run a sampling inference. + * Runs an MCP sampling inference on behalf of an MCP server. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -188,7 +218,7 @@ public CompletableFuture executeSampling(Sessio } /** - * The requestId previously passed to executeSampling that should be cancelled. + * Cancels an in-flight MCP sampling execution by request ID. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -204,7 +234,7 @@ public CompletableFuture cancelSampling } /** - * Mode controlling how MCP server env values are resolved (`direct` or `indirect`). + * Sets how environment-variable values supplied to MCP servers are resolved (direct or indirect). *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -220,7 +250,7 @@ public CompletableFuture setEnvValueMode(Sessio } /** - * Identifies the target session. + * Removes the auto-managed `github` MCP server when present. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -231,7 +261,7 @@ public CompletableFuture removeGitHub() { } /** - * Credential-free authentication identity used to configure GitHub MCP. + * Configures the built-in GitHub MCP server for the session's current auth context. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -240,14 +270,14 @@ public CompletableFuture removeGitHub() { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture configureGitHub(SessionMcpConfigureGitHubParams params) { + CompletableFuture configureGitHub(SessionMcpConfigureGitHubParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.mcp.configureGitHub", _p, SessionMcpConfigureGitHubResult.class); + return caller.invoke("session.mcp.configureGitHub", _p, McpConfigureGitHubResult.class); } /** - * 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. + * Starts an individual MCP server on the live session. Omit `config` for a config-free start-by-name of an already-configured server (reuses the server's already-registered configuration); supply `config` to start from a caller-supplied configuration. Session-scoped and ephemeral: the server is added to this session's running set only and is reaped when the session ends. Does NOT modify persistent user configuration (`mcp.config.*`), so it does not affect future sessions. The server surfaces through `session.mcp.list` and the `session.mcp_servers_loaded` / `session.mcp_server_status_changed` events like any other server. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -263,7 +293,7 @@ public CompletableFuture startServer(SessionMcpStartServerParams params) { } /** - * 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. + * Starts an individual MCP server on the live session. Omit `config` for a config-free start-by-name of an already-configured server (reuses the server's already-registered configuration); supply `config` to start from a caller-supplied configuration. Session-scoped and ephemeral: the server is added to this session's running set only and is reaped when the session ends. Does NOT modify persistent user configuration (`mcp.config.*`), so it does not affect future sessions. The server surfaces through `session.mcp.list` and the `session.mcp_servers_loaded` / `session.mcp_server_status_changed` events like any other server. *

* Accepts the extensible request, including inputs added after the params record. * @@ -278,7 +308,7 @@ public CompletableFuture startServer(SessionMcpStartServerRequest request) } /** - * 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. + * Restarts an individual MCP server on the live session (stops then starts). Omit `config` for a config-free restart-by-name of an already-configured server; supply `config` to restart with a replacement configuration. Session-scoped and ephemeral: does NOT modify persistent user configuration (`mcp.config.*`). *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -294,7 +324,7 @@ public CompletableFuture restartServer(SessionMcpRestartServerParams param } /** - * 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. + * Restarts an individual MCP server on the live session (stops then starts). Omit `config` for a config-free restart-by-name of an already-configured server; supply `config` to restart with a replacement configuration. Session-scoped and ephemeral: does NOT modify persistent user configuration (`mcp.config.*`). *

* Accepts the extensible request, including inputs added after the params record. * @@ -309,7 +339,7 @@ public CompletableFuture restartServer(SessionMcpRestartServerRequest requ } /** - * Server name for an individual MCP server stop. + * Stops an individual MCP server on the session's host. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -325,7 +355,7 @@ public CompletableFuture stopServer(SessionMcpStopServerParams params) { } /** - * Server name for an individual MCP server stop. + * Stops an individual MCP server on the session's host. *

* Accepts the extensible request, including inputs added after the params record. * @@ -340,7 +370,7 @@ public CompletableFuture stopServer(SessionMcpStopServerRequest request) { } /** - * Registration parameters for an external MCP client. + * 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. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -349,14 +379,14 @@ public CompletableFuture stopServer(SessionMcpStopServerRequest request) { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture registerExternalClient(SessionMcpRegisterExternalClientParams params) { + CompletableFuture registerExternalClient(SessionMcpRegisterExternalClientParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); return caller.invoke("session.mcp.registerExternalClient", _p, Void.class); } /** - * Server name identifying the external client to remove. + * 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. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -365,14 +395,14 @@ public CompletableFuture registerExternalClient(SessionMcpRegisterExternal * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture unregisterExternalClient(SessionMcpUnregisterExternalClientParams params) { + CompletableFuture unregisterExternalClient(SessionMcpUnregisterExternalClientParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); return caller.invoke("session.mcp.unregisterExternalClient", _p, Void.class); } /** - * Server name to check running status for. + * Checks whether a named MCP server is currently running on the session's host. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsApi.java index 6b932855b2..35cd4c57a3 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpAppsApi.java @@ -32,7 +32,7 @@ public final class SessionMcpAppsApi { } /** - * MCP server and resource URI to fetch. + * Fetch an MCP resource (typically a `ui://` MCP App bundle, per SEP-1865) from a connected server. Requires the `mcp-apps` session capability. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -48,7 +48,7 @@ public CompletableFuture readResource(SessionM } /** - * MCP server to list app-callable tools for. + * List tools that an MCP App view is allowed to call (SEP-1865 visibility filter). Returns tools whose `_meta.ui.visibility` is unset (default `["model","app"]`) or includes `"app"`. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -64,7 +64,7 @@ public CompletableFuture listTools(SessionMcpApps } /** - * MCP server, tool name, and arguments to invoke from an MCP App view. + * Call an MCP tool from an MCP App view (SEP-1865). Enforces the visibility check that prevents an app iframe from invoking model-only tools. Returns the standard MCP `CallToolResult`. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -80,7 +80,7 @@ public CompletableFuture callTool(SessionMcpAppsCallToolParams params) } /** - * Host context to advertise to MCP App guests. + * Replace the host context returned to MCP App guests on `ui/initialize`. Hosts use this to advertise theme, locale, or other metadata to the guest UI. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -96,7 +96,7 @@ public CompletableFuture setHostContext(SessionMcpAppsSetHostContextParams } /** - * Identifies the target session. + * Read the current host context advertised to MCP App guests. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -107,7 +107,7 @@ public CompletableFuture getHostContext() { } /** - * MCP server to diagnose MCP Apps wiring for. + * Diagnose MCP Apps wiring for a specific MCP server. Reports the session capability, feature-flag state, advertised extension, and how many tools have `_meta.ui` populated. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubParams.java index e46a0af8f0..138ec1d55a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpConfigureGitHubParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionMcpConfigureGitHubParams( +record SessionMcpConfigureGitHubParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Opaque runtime auth info for GitHub MCP configuration. Marked internal: an in-process runtime shape (configureGitHubMcp is a no-op over the wire). */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersApi.java index 45679f83a4..b6c940462e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpHeadersApi.java @@ -31,7 +31,7 @@ public final class SessionMcpHeadersApi { } /** - * MCP headers refresh request id and the host response. + * Responds to a pending MCP dynamic headers refresh request. Hosts that subscribe to `mcp.headers_refresh_required` use this to provide short-lived per-server headers or to indicate that no dynamic headers are available for this refresh. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListConfiguredParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListConfiguredParams.java new file mode 100644 index 0000000000..d4d886b5b0 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListConfiguredParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Identifies the target session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionMcpListConfiguredParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListConfiguredResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListConfiguredResult.java new file mode 100644 index 0000000000..b54b5b238c --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpListConfiguredResult.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; + +/** + * Effective MCP configuration with optional live observations from matching already materialized servers. + * + * @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 SessionMcpListConfiguredResult( + /** Effective configured MCP servers. */ + @JsonProperty("servers") List servers +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java index 835962df39..3910d287eb 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthApi.java @@ -32,7 +32,7 @@ public final class SessionMcpOauthApi { } /** - * Pending MCP OAuth request ID and host-provided token or cancellation response. + * Resolves a pending MCP OAuth request with a host-provided token or cancellation. The pending request is emitted as mcp.oauth_required with the data necessary to authorize the request. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -48,7 +48,7 @@ public CompletableFuture handlePendin } /** - * Identifies the MCP server whose persisted OAuth credentials were updated. + * Notifies the session that MCP OAuth authentication succeeded and updated credentials were persisted, so cached tool definitions can be refreshed. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -64,7 +64,7 @@ public CompletableFuture authenticationStateChanged(SessionMcpOauthAuthent } /** - * Effect-free preparation bound to the existing local session, requester and installation, with frozen options. + * Prepares an inert, expiring owned OAuth login bound to the original session requester and exact installation. Does not activate, connect, read credentials or open a browser. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -80,7 +80,7 @@ public CompletableFuture prepareLogin(Session } /** - * Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback success-page copy, and static OAuth client selection. + * Starts OAuth authentication for a remote MCP server. Owned servers require the original one-use prepareLogin handle and exact installation ID; manual servers retain the existing direct login behaviour. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -96,7 +96,7 @@ public CompletableFuture login(SessionMcpOauthLoginP } /** - * Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback success-page copy, and static OAuth client selection. + * Starts OAuth authentication for a remote MCP server. Owned servers require the original one-use prepareLogin handle and exact installation ID; manual servers retain the existing direct login behaviour. *

* Accepts the extensible request, including inputs added after the params record. * @@ -111,7 +111,23 @@ public CompletableFuture login(SessionMcpOauthLoginR } /** - * Remote MCP server name for a passive OAuth status probe. + * Completes a runtime-managed MCP OAuth login after the authorization server redirects to a host-managed callback URL. + *

+ * 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 complete(SessionMcpOauthCompleteParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.mcp.oauth.complete", _p, Void.class); + } + + /** + * Passively probes a configured remote MCP server to classify whether OAuth is required or a cached/override token is accepted. Does not start OAuth, emit pending OAuth requests, or mutate MCP connection state. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -127,7 +143,7 @@ public CompletableFuture probe(SessionMcpOauthProbeParams p } /** - * Remote MCP server name for a passive OAuth status probe. + * Passively probes a configured remote MCP server to classify whether OAuth is required or a cached/override token is accepted. Does not start OAuth, emit pending OAuth requests, or mutate MCP connection state. *

* Accepts the extensible request, including inputs added after the params record. * @@ -142,7 +158,7 @@ public CompletableFuture probe(SessionMcpOauthProbeRequest } /** - * Targets only the original prepared/applying owned login on this exact session requester. + * Cancels the exact owned OAuth login issued to this original session requester, without clearing shared credentials. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -158,7 +174,7 @@ public CompletableFuture cancelLogin(SessionMc } /** - * Pending MCP OAuth request id to respond to. + * Responds to a pending MCP OAuth authorization request by its request id. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthCompleteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthCompleteParams.java new file mode 100644 index 0000000000..be283f0b36 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthCompleteParams.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; + +/** + * Host-delivered callback for a runtime-managed MCP OAuth login. + * + * @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 SessionMcpOauthCompleteParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Opaque identifier returned by session.mcp.oauth.login for the pending external callback. */ + @JsonProperty("authorizationId") String authorizationId, + /** 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. */ + @JsonProperty("callbackUrl") String callbackUrl +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginParams.java index d9234d5587..6f16286adf 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginParams.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback success-page copy, and static OAuth client selection. + * Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback handling, and static OAuth client selection. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginRequest.java index 90864e7dec..82219d8042 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginRequest.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginRequest.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback success-page copy, and static OAuth client selection. + * Remote MCP server name and optional overrides controlling reauthentication, OAuth client display name, callback handling, and static OAuth client selection. *

* Required inputs are constructor arguments. Optional inputs have fluent setters. * @@ -58,6 +58,10 @@ public final class SessionMcpOauthLoginRequest { @JsonProperty("grantType") private McpOauthLoginGrantType grantType; + /** 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. */ + @JsonProperty("redirectUri") + private String redirectUri; + /** Required for owned login. Consumes the exact prepareLogin handle once. Set forceReauth and display options during preparation, not consumption. */ @JsonProperty("loginId") private String loginId; @@ -147,6 +151,15 @@ public McpOauthLoginGrantType getGrantType() { return grantType; } + /** + * Returns the {@code redirectUri} property. + * + * @return 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. + */ + public String getRedirectUri() { + return redirectUri; + } + /** * Returns the {@code loginId} property. * @@ -242,6 +255,17 @@ public SessionMcpOauthLoginRequest setGrantType(McpOauthLoginGrantType value) { return this; } + /** + * Sets the {@code redirectUri} property. + * + * @param value 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. + * @return this request + */ + public SessionMcpOauthLoginRequest setRedirectUri(String value) { + this.redirectUri = value; + return this; + } + /** * Sets the {@code loginId} property. * diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginResult.java index aaecf8a64a..5063ec97ca 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpOauthLoginResult.java @@ -28,7 +28,9 @@ public record SessionMcpOauthLoginResult( @JsonProperty("loginId") String loginId, /** Explicit outcome for owned sign-in. Manual callers retain their legacy response shape. */ @JsonProperty("status") McpOwnedOauthLoginStatus status, - /** 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. When present, the runtime starts the callback listener before returning and continues the flow in the background; completion is signaled via session.mcp_server_status_changed. */ - @JsonProperty("authorizationUrl") String authorizationUrl + /** 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. */ + @JsonProperty("authorizationUrl") String authorizationUrl, + /** 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. */ + @JsonProperty("authorizationId") String authorizationId ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsApi.java new file mode 100644 index 0000000000..ec60f552ca --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsApi.java @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.github.copilot.CopilotExperimental; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code mcp.prompts} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionMcpPromptsApi { + + private static final com.fasterxml.jackson.databind.ObjectMapper MAPPER = RpcMapper.INSTANCE; + + private final RpcCaller caller; + private final String sessionId; + + /** @param caller the RPC transport function */ + SessionMcpPromptsApi(RpcCaller caller, String sessionId) { + this.caller = caller; + this.sessionId = sessionId; + } + + /** + * Enumerate one page of prompts a connected MCP server exposes (proxies MCP `prompts/list`). Pass `cursor` to continue from a prior result's `nextCursor`. + *

+ * 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 list(SessionMcpPromptsListParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.mcp.prompts.list", _p, SessionMcpPromptsListResult.class); + } + + /** + * Get a prompt's messages from a connected MCP server (proxies MCP `prompts/get`). Content is preserved as opaque JSON. Does not send messages to the model, execute tools, or fetch referenced resources. + *

+ * 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 get(SessionMcpPromptsGetParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.mcp.prompts.get", _p, SessionMcpPromptsGetResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsGetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsGetParams.java new file mode 100644 index 0000000000..507b55d7d7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsGetParams.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; + +/** + * MCP server, prompt name, and optional string-valued arguments. + * + * @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 SessionMcpPromptsGetParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Name of the MCP server hosting the prompt */ + @JsonProperty("serverName") String serverName, + /** The programmatic name of the prompt */ + @JsonProperty("promptName") String promptName, + /** String-valued arguments to pass to the prompt */ + @JsonProperty("arguments") Map arguments +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsGetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsGetResult.java new file mode 100644 index 0000000000..26f1ee0186 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsGetResult.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 java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * Prompt messages returned by the MCP server without sending them to the 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 SessionMcpPromptsGetResult( + /** Description of the prompt */ + @JsonProperty("description") String description, + /** Ordered prompt messages */ + @JsonProperty("messages") List messages, + /** MCP result metadata */ + @JsonProperty("_meta") Map meta, + /** Server-provided non-standard result fields */ + @JsonProperty("additionalProperties") Map additionalProperties +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsListParams.java new file mode 100644 index 0000000000..e1d21eef1a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsListParams.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; + +/** + * MCP server whose prompts to enumerate. + * + * @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 SessionMcpPromptsListParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Name of the MCP server whose prompts to enumerate */ + @JsonProperty("serverName") String serverName, + /** Opaque MCP pagination cursor from a prior `nextCursor` value */ + @JsonProperty("cursor") String cursor +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsListResult.java new file mode 100644 index 0000000000..188cb5f862 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpPromptsListResult.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 java.util.List; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * One page of prompts advertised by the named MCP server. + * + * @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 SessionMcpPromptsListResult( + /** Prompts advertised by the server */ + @JsonProperty("prompts") List prompts, + /** Opaque cursor for the next page, if the server has more prompts */ + @JsonProperty("nextCursor") String nextCursor, + /** MCP result metadata */ + @JsonProperty("_meta") Map meta, + /** Server-provided non-standard result fields */ + @JsonProperty("additionalProperties") Map additionalProperties +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRegisterExternalClientParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRegisterExternalClientParams.java index ba5cdc353e..f2d457cdd5 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRegisterExternalClientParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpRegisterExternalClientParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionMcpRegisterExternalClientParams( +record SessionMcpRegisterExternalClientParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Logical server name for the external client */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigParams.java index b93733f0e3..409e75308e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpReloadWithConfigParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionMcpReloadWithConfigParams( +record SessionMcpReloadWithConfigParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Opaque runtime MCP reload configuration. Marked internal: an in-process runtime shape (reloadMcpServers throws over the wire). */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesApi.java index c1a30e135d..e81f50eb80 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpResourcesApi.java @@ -31,7 +31,7 @@ public final class SessionMcpResourcesApi { } /** - * MCP server and resource URI to fetch. + * Fetch an MCP resource from a connected server by URI (proxies MCP `resources/read`). *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture read(SessionMcpResources } /** - * MCP server whose resources to enumerate. + * Enumerate one page of resources a connected MCP server exposes (proxies MCP `resources/list`). Pass `cursor` to continue from a prior result's `nextCursor`. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -63,7 +63,7 @@ public CompletableFuture list(SessionMcpResources } /** - * MCP server whose resource templates to enumerate. + * Enumerate one page of resource templates a connected MCP server exposes (proxies MCP `resources/templates/list`). Pass `cursor` to continue from a prior result's `nextCursor`. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetConnectedIdeInfoParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetConnectedIdeInfoParams.java new file mode 100644 index 0000000000..38e53b55d6 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpSetConnectedIdeInfoParams.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; + +/** + * Records which IDE the host is connected to, or clears 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 SessionMcpSetConnectedIdeInfoParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** The connected IDE. Null or omitted clears the recorded IDE, which is how a host reports that it is disconnected. */ + @JsonProperty("ide") SessionConnectedIdeInfo ide +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpUnregisterExternalClientParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpUnregisterExternalClientParams.java index 390e998527..d58cc1d58e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpUnregisterExternalClientParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMcpUnregisterExternalClientParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionMcpUnregisterExternalClientParams( +record SessionMcpUnregisterExternalClientParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Server name of the external client to unregister */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataApi.java index 8d7f3dd753..bf97a0ec39 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataApi.java @@ -31,7 +31,7 @@ public final class SessionMetadataApi { } /** - * Identifies the target session. + * Returns a snapshot of the session's identifying metadata, mode, agent, and remote info. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture snapshot() { } /** - * Identifies the target session. + * Returns the client-owned string metadata persisted with this local session. The metadata is not included in model context, events, telemetry, snapshots, or remote exports. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -53,7 +53,7 @@ public CompletableFuture getClientMetadata() { } /** - * 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. + * Atomically patches the client-owned string metadata persisted with this local session and returns the committed bag. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -69,7 +69,7 @@ public CompletableFuture updateClientMetadata(SessionMetadataUpd } /** - * Identifies the target session. + * Reports whether the local session is currently processing user/agent messages. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -80,7 +80,7 @@ public CompletableFuture isProcessing() { } /** - * Identifies the target session. + * Returns a snapshot of activity flags for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -91,7 +91,7 @@ public CompletableFuture activity() { } /** - * Model identifier and token limits used to compute the context-info breakdown. + * Returns the token breakdown for the session's current context window for a given model. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -107,7 +107,7 @@ public CompletableFuture contextInfo(SessionMe } /** - * Identifies the target session. + * Returns the experimental per-source attribution breakdown of the session's current context window as a flat list of entries (skills, subagents, MCP servers, built-in tools, plugin rollups, system/tool-definition costs, with nesting via parentId), plus the successful compaction count. The heaviest individual messages are available separately via `metadata.getContextHeaviestMessages`. Returns null until the session has initialized its system prompt and tool metadata. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -118,7 +118,7 @@ public CompletableFuture getContextA } /** - * Parameters for the heaviest-messages query. + * Returns the largest individual messages currently in the session's context window, most-expensive first. Companion to `metadata.getContextAttribution`. Returns an empty list until the session has initialized. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -134,7 +134,7 @@ public CompletableFuture getCon } /** - * Updated working-directory/git context to record on the session. + * Records a working-directory/git context change and emits a `session.context_changed` event. 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): a local session's working directory is authoritative and is moved via `metadata.setWorkingDirectory` (or an SDK `session.resume` that supplies a `workingDirectory`), not by this method. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -150,7 +150,7 @@ public CompletableFuture recordContextChange(SessionMetadataRecordContextC } /** - * 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. + * Updates the session's working directory. For local sessions the target is validated first (an absolute path that exists on disk) and the permission primary directory is re-based; a rejected validation fails the call before any session state changes. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -166,7 +166,7 @@ public CompletableFuture setWorkingDir } /** - * Model identifier to use when re-tokenizing the session's existing messages. + * Re-tokenizes the session's existing messages against a model and returns aggregate token totals. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoParams.java index 0c5b909db7..a190c0d194 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoParams.java @@ -26,11 +26,13 @@ public record SessionMetadataContextInfoParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, - /** Maximum prompt tokens allowed by the target model. Pass 0 to use the runtime default. */ + /** Advertised prompt allowance. Pass 0 to resolve the selected model and context tier from the session. */ @JsonProperty("promptTokenLimit") Long promptTokenLimit, - /** Maximum output tokens allowed by the target model. Pass 0 if unknown. */ + /** 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. */ @JsonProperty("outputTokenLimit") Long outputTokenLimit, /** Model identifier used for tokenization. Omit to use the session default. Used both for token counting and to compute display values. */ - @JsonProperty("selectedModel") String selectedModel + @JsonProperty("selectedModel") String selectedModel, + /** Provider owning selectedModel. Omit to use the current provider for the current model, or deterministic legacy lookup for a different model. */ + @JsonProperty("providerId") String providerId ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoResult.java index 6472956026..4415e14096 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataContextInfoResult.java @@ -34,6 +34,10 @@ public record SessionMetadataContextInfoResult( public record SessionMetadataContextInfoResultContextInfo( /** The model used for token counting */ @JsonProperty("modelName") String modelName, + /** Provider whose catalog supplies this context window. Omitted when provider identity is unavailable. */ + @JsonProperty("provider") ModelProviderRef provider, + /** Provider-owned model display label, or the Auto routing label. Presentation only; modelName remains the tokenization identity. */ + @JsonProperty("displayModelName") String displayModelName, /** Tokens consumed by the system prompt */ @JsonProperty("systemTokens") Long systemTokens, /** Tokens consumed by user/assistant/tool messages */ @@ -44,13 +48,13 @@ public record SessionMetadataContextInfoResultContextInfo( @JsonProperty("mcpToolsTokens") Long mcpToolsTokens, /** Sum of system, conversation and tool-definition tokens */ @JsonProperty("totalTokens") Long totalTokens, - /** Maximum prompt tokens allowed by the model (or DEFAULT_TOKEN_LIMIT if unspecified) */ + /** 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. */ @JsonProperty("promptTokenLimit") Long promptTokenLimit, /** Token count at which background compaction starts (configurable percentage of promptTokenLimit) */ @JsonProperty("compactionThreshold") Long compactionThreshold, - /** Prompt token limit plus the model's full output token limit. */ + /** Advertised prompt allowance for the selected context tier, without adding output tokens. The denominator for context-usage displays. */ @JsonProperty("limit") Long limit, - /** Output reserve plus tokens after the buffer-exhaustion blocking threshold (default 95%) */ + /** Output reservation overlapping the displayed prompt allowance plus tokens after the effective input budget's buffer-exhaustion blocking threshold (default 95%). */ @JsonProperty("bufferTokens") Long bufferTokens ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java index c27f37afb0..44713111cf 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionMetadataGetContextAttributionResult.java @@ -40,11 +40,11 @@ public record SessionMetadataGetContextAttributionResultContextAttribution( @JsonProperty("modelId") String modelId, /** 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). */ @JsonProperty("modelSource") String modelSource, - /** Maximum prompt tokens the resolved model accepts — the denominator for a `##k/###k` context-usage display. Mirrors `SessionContextInfo.promptTokenLimit`. */ + /** Effective input budget after reserving requested output against the combined context ceiling. Mirrors `SessionContextInfo.promptTokenLimit`. */ @JsonProperty("promptTokenLimit") Long promptTokenLimit, - /** Prompt limit plus the model's output reserve: the full context window `categories.freeSpace` and `categories.buffer` are measured against. Mirrors `SessionContextInfo.limit`. */ + /** 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`. */ @JsonProperty("limit") Long limit, - /** Output reserve plus the tokens past the buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. */ + /** Output reservation overlapping the displayed prompt allowance plus the tokens past the effective input budget's buffer-exhaustion blocking threshold. Mirrors `SessionContextInfo.bufferTokens`. */ @JsonProperty("bufferTokens") Long bufferTokens, /** Token count at which background compaction starts. Mirrors `SessionContextInfo.compactionThreshold`. */ @JsonProperty("compactionThreshold") Long compactionThreshold, @@ -72,7 +72,7 @@ public record SessionMetadataGetContextAttributionResultContextAttributionCatego @JsonProperty("messages") Long messages, /** Remaining unused window capacity (clamped at 0). */ @JsonProperty("freeSpace") Long freeSpace, - /** Output reserve plus post-blocking-threshold buffer. */ + /** Overlapping output reservation plus post-blocking-threshold buffer. */ @JsonProperty("buffer") Long buffer ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeApi.java index 0f4989a920..8c2c2d66da 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeApi.java @@ -31,7 +31,7 @@ public final class SessionModeApi { } /** - * Identifies the target session. + * Gets the current agent interaction mode. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture get() { } /** - * Agent interaction mode to apply to the session. + * Sets the current agent interaction mode. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeSetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeSetParams.java index 9a20862a13..9c11577700 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeSetParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModeSetParams.java @@ -36,6 +36,8 @@ public record SessionModeSetParams( @JsonProperty("planModelConfigured") Boolean planModelConfigured, /** Dedicated model to use in plan mode, when configured. */ @JsonProperty("planModel") String planModel, + /** Provider owning planModel. Omit for legacy bare-model selection. */ + @JsonProperty("planModelProviderId") String planModelProviderId, /** Reasoning effort to use with the dedicated plan model. */ @JsonProperty("planReasoningEffort") String planReasoningEffort, /** Context tier to use with the dedicated plan model. */ 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 7afe40eb0b..1c5c1c3d39 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 @@ -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; @@ -31,7 +32,7 @@ public final class SessionModelApi { } /** - * Identifies the target session. + * Gets the session's authoritative model snapshot, including the committed Auto preference and any newer unclaimed Auto preference waiting for a future user turn. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +43,7 @@ public CompletableFuture getCurrent() { } /** - * Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. + * Switches the session to a model and optional reasoning configuration. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -58,7 +59,22 @@ public CompletableFuture switchTo(SessionModelSwitch } /** - * An Auto preference request for the session. This updates Auto configuration only; it does not change the selected model to `auto`. + * Switches the session to a model and optional reasoning configuration. + *

+ * 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 switchTo(SessionModelSwitchToRequest request) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(Objects.requireNonNull(request, "request")); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.model.switchTo", _p, SessionModelSwitchToResult.class); + } + + /** + * Requests an Auto preference change without changing the session's selected model. The latest unclaimed request wins; the runtime commits it only after a later prompt using the `auto` model mints a usable model and token pair. A `pending` response confirms that the request was accepted, not that it committed. Observe eventual success through `session.model_change`, failure through the ephemeral `session.auto_tier_switch_failed` event, or current unclaimed state through `session.model.getCurrent`. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -74,7 +90,23 @@ public CompletableFuture switchAutoTier(Sessio } /** - * Managed, repository, and CLI model overrides to overlay onto the session at startup. + * Resolves and applies organization-managed and repository model overlays. + *

+ * 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 + CompletableFuture applyStartupOverlay(SessionModelApplyStartupOverlayParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.model.applyStartupOverlay", _p, ModelSwitchToResult.class); + } + + /** + * Clears an unchanged startup model/provider seed before default-model resolution. Never clears a resumed or user-selected model. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -83,14 +115,14 @@ public CompletableFuture switchAutoTier(Sessio * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture applyStartupOverlay(SessionModelApplyStartupOverlayParams params) { + CompletableFuture clearStartupSeed(SessionModelClearStartupSeedParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.model.applyStartupOverlay", _p, SessionModelApplyStartupOverlayResult.class); + return caller.invoke("session.model.clearStartupSeed", _p, ModelClearStartupSeedResult.class); } /** - * 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. + * Replaces or clears the host-supplied model allowlist for a running session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -106,7 +138,7 @@ public CompletableFuture setAllowedModels(Se } /** - * Reasoning effort level to apply to the currently selected model. + * Updates the session's reasoning effort without changing the selected model. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -122,7 +154,7 @@ public CompletableFuture setReasoningEffor } /** - * Optional listing options. + * 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. *

* Invokes the method with no params, applying the runtime defaults. * @@ -135,7 +167,7 @@ public CompletableFuture list() { } /** - * Optional listing options. + * 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. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApplyStartupOverlayParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApplyStartupOverlayParams.java index 73ae210e78..e4aadb58f1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApplyStartupOverlayParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelApplyStartupOverlayParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionModelApplyStartupOverlayParams( +record SessionModelApplyStartupOverlayParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Model required by device-managed policy, when configured. */ @@ -36,6 +36,8 @@ public record SessionModelApplyStartupOverlayParams( @JsonProperty("autoTier") AutoTier autoTier, /** Model selected by repository settings, when configured. */ @JsonProperty("repoModel") String repoModel, + /** Provider owning the repository-selected model. Omitted repository provider identity retains legacy bare-model behavior. */ + @JsonProperty("repoModelProviderId") String repoModelProviderId, /** Reasoning effort selected by repository settings, when configured. */ @JsonProperty("repoReasoningEffort") String repoReasoningEffort, /** Context tier selected by repository settings, when configured. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelClearStartupSeedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelClearStartupSeedParams.java new file mode 100644 index 0000000000..d927c746ca --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelClearStartupSeedParams.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; + +/** + * Request parameters for the {@code session.model.clearStartupSeed} RPC method. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SessionModelClearStartupSeedParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + @JsonProperty("expectedModel") String expectedModel, + /** Provider captured with the startup seed; omission matches only an unqualified seed. */ + @JsonProperty("expectedProviderId") String expectedProviderId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentResult.java index 23a9788540..28ef368715 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelGetCurrentResult.java @@ -26,6 +26,12 @@ public record SessionModelGetCurrentResult( /** Currently active model identifier */ @JsonProperty("modelId") String modelId, + /** Provider selected for this model. Together with modelId, identifies a catalog entry even when providers serve the same model. Omitted for unattributed legacy selections. */ + @JsonProperty("providerId") String providerId, + /** 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. */ + @JsonProperty("planBaseModelId") String planBaseModelId, + /** Provider of planBaseModelId, when that saved selection is provider-qualified. */ + @JsonProperty("planBaseProviderId") String planBaseProviderId, /** 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. */ @JsonProperty("reasoningEffort") String reasoningEffort, /** Context tier for models that support multiple context-window sizes. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java index 1c73b4b22f..3a18d77488 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelListResult.java @@ -33,6 +33,8 @@ public record SessionModelListResult( /** 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. */ @JsonProperty("modelPriceCategories") List modelPriceCategories, /** Per-quota snapshots returned alongside the model list, keyed by quota type. */ - @JsonProperty("quotaSnapshots") Map quotaSnapshots + @JsonProperty("quotaSnapshots") Map quotaSnapshots, + /** Ordered Auto routing preferences discovered for this session's account. */ + @JsonProperty("auto") AutoTierMetadata auto ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java index a37eebd2f6..0f30a514ca 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToParams.java @@ -26,7 +26,7 @@ public record SessionModelSwitchToParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, - /** Model selection id to switch to, as returned by `list`. A bare id (e.g. `claude-sonnet-4.6`) names a Copilot (CAPI) model; a provider-qualified id (`provider/id`, e.g. `acme/claude-sonnet`) targets a registry BYOK model. */ + /** 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. */ @JsonProperty("modelId") String modelId, /** 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`. */ @JsonProperty("autoTier") AutoTier autoTier, diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToRequest.java new file mode 100644 index 0000000000..09168fcb52 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionModelSwitchToRequest.java @@ -0,0 +1,410 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All 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.Objects; +import javax.annotation.processing.Generated; + +/** + * Target model identifier and optional reasoning effort, summary, capability overrides, and context tier. + *

+ * 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 SessionModelSwitchToRequest { + + /** 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. */ + @JsonProperty("modelId") + private final String modelId; + + /** 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`. */ + @JsonProperty("autoTier") + private AutoTier autoTier; + + /** 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. */ + @JsonProperty("reasoningEffort") + private String reasoningEffort; + + /** Reasoning summary mode to request for supported model clients */ + @JsonProperty("reasoningSummary") + private ReasoningSummary reasoningSummary; + + /** Output verbosity level to request for supported models */ + @JsonProperty("verbosity") + private Verbosity verbosity; + + /** Override individual model capabilities resolved by the runtime */ + @JsonProperty("modelCapabilities") + private ModelCapabilitiesOverride modelCapabilities; + + /** 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. */ + @JsonProperty("contextTier") + private ContextTier contextTier; + + /** 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. */ + @JsonProperty("source") + private ModelChangeSource source; + + /** 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). */ + @JsonProperty("deferIfModelChangeQueued") + private Boolean deferIfModelChangeQueued; + + /** Explicit response to a model-switch compaction preflight. Omit to request a confirmation projection when compaction is necessary. */ + @JsonProperty("compactionDecision") + private String compactionDecision; + + /** When true, evaluate context-window compaction policy before applying the switch. */ + @JsonProperty("runCompactionPreflight") + private Boolean runCompactionPreflight; + + /** Optional repository settings scope to persist after the switch commits. */ + @JsonProperty("repoScope") + private String repoScope; + + /** Settings scope used when persisting the selected model. */ + @JsonProperty("modelChangeScope") + private String modelChangeScope; + + /** Require the target to be currently available and enabled before applying the switch. */ + @JsonProperty("requireAvailable") + private Boolean requireAvailable; + + /** Optional settings context and explicit-override flags used to persist a picker selection. */ + @JsonProperty("pickerPersistence") + private ModelPickerPersistenceRequest pickerPersistence; + + /** 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. */ + @JsonProperty("providerId") + private String providerId; + + /** + * Creates a request with its required inputs. + * + * @param modelId 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. + */ + public SessionModelSwitchToRequest(String modelId) { + this.modelId = Objects.requireNonNull(modelId, "modelId"); + } + + /** + * Returns the {@code modelId} property. + * + * @return 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. + */ + public String getModelId() { + return modelId; + } + + /** + * Returns the {@code autoTier} property. + * + * @return 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`. + */ + public AutoTier getAutoTier() { + return autoTier; + } + + /** + * Returns the {@code reasoningEffort} property. + * + * @return 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. + */ + public String getReasoningEffort() { + return reasoningEffort; + } + + /** + * Returns the {@code reasoningSummary} property. + * + * @return Reasoning summary mode to request for supported model clients + */ + public ReasoningSummary getReasoningSummary() { + return reasoningSummary; + } + + /** + * Returns the {@code verbosity} property. + * + * @return Output verbosity level to request for supported models + */ + public Verbosity getVerbosity() { + return verbosity; + } + + /** + * Returns the {@code modelCapabilities} property. + * + * @return Override individual model capabilities resolved by the runtime + */ + public ModelCapabilitiesOverride getModelCapabilities() { + return modelCapabilities; + } + + /** + * Returns the {@code contextTier} property. + * + * @return 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. + */ + public ContextTier getContextTier() { + return contextTier; + } + + /** + * Returns the {@code source} property. + * + * @return 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. + */ + public ModelChangeSource getSource() { + return source; + } + + /** + * Returns the {@code deferIfModelChangeQueued} property. + * + * @return 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). + */ + public Boolean getDeferIfModelChangeQueued() { + return deferIfModelChangeQueued; + } + + /** + * Returns the {@code compactionDecision} property. + * + * @return Explicit response to a model-switch compaction preflight. Omit to request a confirmation projection when compaction is necessary. + */ + public String getCompactionDecision() { + return compactionDecision; + } + + /** + * Returns the {@code runCompactionPreflight} property. + * + * @return When true, evaluate context-window compaction policy before applying the switch. + */ + public Boolean getRunCompactionPreflight() { + return runCompactionPreflight; + } + + /** + * Returns the {@code repoScope} property. + * + * @return Optional repository settings scope to persist after the switch commits. + */ + public String getRepoScope() { + return repoScope; + } + + /** + * Returns the {@code modelChangeScope} property. + * + * @return Settings scope used when persisting the selected model. + */ + public String getModelChangeScope() { + return modelChangeScope; + } + + /** + * Returns the {@code requireAvailable} property. + * + * @return Require the target to be currently available and enabled before applying the switch. + */ + public Boolean getRequireAvailable() { + return requireAvailable; + } + + /** + * Returns the {@code pickerPersistence} property. + * + * @return Optional settings context and explicit-override flags used to persist a picker selection. + */ + public ModelPickerPersistenceRequest getPickerPersistence() { + return pickerPersistence; + } + + /** + * Returns the {@code providerId} property. + * + * @return 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. + */ + public String getProviderId() { + return providerId; + } + + /** + * Sets the {@code autoTier} property. + * + * @param value 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`. + * @return this request + */ + public SessionModelSwitchToRequest setAutoTier(AutoTier value) { + this.autoTier = value; + return this; + } + + /** + * Sets the {@code reasoningEffort} property. + * + * @param value 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. + * @return this request + */ + public SessionModelSwitchToRequest setReasoningEffort(String value) { + this.reasoningEffort = value; + return this; + } + + /** + * Sets the {@code reasoningSummary} property. + * + * @param value Reasoning summary mode to request for supported model clients + * @return this request + */ + public SessionModelSwitchToRequest setReasoningSummary(ReasoningSummary value) { + this.reasoningSummary = value; + return this; + } + + /** + * Sets the {@code verbosity} property. + * + * @param value Output verbosity level to request for supported models + * @return this request + */ + public SessionModelSwitchToRequest setVerbosity(Verbosity value) { + this.verbosity = value; + return this; + } + + /** + * Sets the {@code modelCapabilities} property. + * + * @param value Override individual model capabilities resolved by the runtime + * @return this request + */ + public SessionModelSwitchToRequest setModelCapabilities(ModelCapabilitiesOverride value) { + this.modelCapabilities = value; + return this; + } + + /** + * Sets the {@code contextTier} property. + * + * @param value 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. + * @return this request + */ + public SessionModelSwitchToRequest setContextTier(ContextTier value) { + this.contextTier = value; + return this; + } + + /** + * Sets the {@code source} property. + * + * @param value 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. + * @return this request + */ + public SessionModelSwitchToRequest setSource(ModelChangeSource value) { + this.source = value; + return this; + } + + /** + * Sets the {@code deferIfModelChangeQueued} property. + * + * @param value 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). + * @return this request + */ + public SessionModelSwitchToRequest setDeferIfModelChangeQueued(Boolean value) { + this.deferIfModelChangeQueued = value; + return this; + } + + /** + * Sets the {@code compactionDecision} property. + * + * @param value Explicit response to a model-switch compaction preflight. Omit to request a confirmation projection when compaction is necessary. + * @return this request + */ + public SessionModelSwitchToRequest setCompactionDecision(String value) { + this.compactionDecision = value; + return this; + } + + /** + * Sets the {@code runCompactionPreflight} property. + * + * @param value When true, evaluate context-window compaction policy before applying the switch. + * @return this request + */ + public SessionModelSwitchToRequest setRunCompactionPreflight(Boolean value) { + this.runCompactionPreflight = value; + return this; + } + + /** + * Sets the {@code repoScope} property. + * + * @param value Optional repository settings scope to persist after the switch commits. + * @return this request + */ + public SessionModelSwitchToRequest setRepoScope(String value) { + this.repoScope = value; + return this; + } + + /** + * Sets the {@code modelChangeScope} property. + * + * @param value Settings scope used when persisting the selected model. + * @return this request + */ + public SessionModelSwitchToRequest setModelChangeScope(String value) { + this.modelChangeScope = value; + return this; + } + + /** + * Sets the {@code requireAvailable} property. + * + * @param value Require the target to be currently available and enabled before applying the switch. + * @return this request + */ + public SessionModelSwitchToRequest setRequireAvailable(Boolean value) { + this.requireAvailable = value; + return this; + } + + /** + * Sets the {@code pickerPersistence} property. + * + * @param value Optional settings context and explicit-override flags used to persist a picker selection. + * @return this request + */ + public SessionModelSwitchToRequest setPickerPersistence(ModelPickerPersistenceRequest value) { + this.pickerPersistence = value; + return this; + } + + /** + * Sets the {@code providerId} property. + * + * @param value 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. + * @return this request + */ + public SessionModelSwitchToRequest setProviderId(String value) { + this.providerId = value; + return this; + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameApi.java index 9bfc5bea59..aeefa0df31 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionNameApi.java @@ -31,7 +31,7 @@ public final class SessionNameApi { } /** - * Identifies the target session. + * Gets the session's friendly name. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture get() { } /** - * New friendly name to apply to the session. + * Sets the session's friendly name. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -58,7 +58,7 @@ public CompletableFuture set(SessionNameSetParams params) { } /** - * Auto-generated session summary to apply as the session's name when no user-set name exists. + * Persists an auto-generated session summary as the session's name when no user-set name exists. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. 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 dbb3080cdb..5d14a1458a 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 @@ -49,6 +49,8 @@ public record SessionOpenOptions( @JsonProperty("expAssignments") Object expAssignments, /** Opt-in: self-fetch and enforce enterprise managed settings, including managed hook policies, at session bootstrap. */ @JsonProperty("enableManagedSettings") Boolean enableManagedSettings, + /** Opt in to enforcing non-overridable managed model controls on session model, Auto-tier, reasoning-effort, and context-tier changes. Managed defaults still apply when omitted; this option only turns conflicting changes into errors. */ + @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, /** 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. */ @@ -63,6 +65,8 @@ public record SessionOpenOptions( @JsonProperty("provider") ProviderConfig provider, /** Options scoped to the built-in CAPI (Copilot API) provider. */ @JsonProperty("capi") CapiSessionOptions capi, + /** Whether a CLI host explicitly requested the initial Auto preference. False preserves a settings-derived preference without validating availability during creation; execution still validates it. Defaults to true and is ignored for non-CLI callers. */ + @JsonProperty("autoTierIsExplicit") Boolean autoTierIsExplicit, /** Named BYOK provider connections, additive to CAPI auth. Combining with `provider` is rejected. */ @JsonProperty("providers") List providers, /** BYOK model definitions added to the selectable model list, each referencing a provider name. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsApi.java index 32d4b76908..06986da2ff 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionOptionsApi.java @@ -31,7 +31,7 @@ public final class SessionOptionsApi { } /** - * Patch of mutable session options to apply to the running session. + * Patches the genuinely-mutable subset of session options. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsApi.java index aed2de53fb..eb4cc591e8 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsApi.java @@ -44,7 +44,7 @@ public final class SessionPermissionsApi { } /** - * Patch of permission policy fields to apply (omit a field to leave it unchanged). + * Replaces selected permission policy fields (rules, paths, URLs, exclusions, allow-all flags) on the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -60,7 +60,7 @@ public CompletableFuture configure(SessionPer } /** - * Pending permission request ID and the decision to apply (approve/reject and scope). + * Provides a decision for a pending tool permission request. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -76,7 +76,7 @@ public CompletableFuture } /** - * No parameters; returns currently-pending permission requests for the session. + * Reconstructs the set of pending tool permission requests from the session's event history. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -87,7 +87,7 @@ public CompletableFuture pendingRequest } /** - * Allow-all toggle for tool permission requests, with an optional telemetry source. + * Enables or disables automatic approval of tool permission requests for the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -103,7 +103,7 @@ public CompletableFuture setApproveAll(Se } /** - * Permission mode to apply for the session. + * Sets the permission mode for the session. `manual` follows the normal approval flow, `assisted` attaches LLM safety recommendations, and `allow-all` automatically approves permission requests. The result returns the authoritative post-mutation mode so callers can update local state without racing the `session.permissions_changed` notification. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -119,7 +119,7 @@ public CompletableFuture setMode(SessionPermiss } /** - * No parameters. + * Returns the current permission mode for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -130,7 +130,7 @@ public CompletableFuture getMode() { } /** - * Scope and add/remove instructions for modifying session- or location-scoped permission rules. + * Adds or removes session-scoped or location-scoped permission rules. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -146,7 +146,7 @@ public CompletableFuture modifyRules(Sessio } /** - * Toggles whether permission prompts should be bridged into session events for this client. + * Sets whether the client wants permission prompts bridged into session events. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -162,7 +162,7 @@ public CompletableFuture setRequired(Sessio } /** - * Clears session-scoped tool approvals and optionally clears location-scoped approvals and exact session-approved paths. + * Clears session-scoped tool approvals and, for full resets, exact session-approved paths. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -178,7 +178,7 @@ public CompletableFuture resetSes } /** - * Notification payload describing the permission prompt that the client just rendered. + * Notifies the runtime that a permission prompt UI has been shown to the user. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustApi.java index 55bac08941..741e4ed737 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsFolderTrustApi.java @@ -31,7 +31,7 @@ public final class SessionPermissionsFolderTrustApi { } /** - * Folder path to check for trust. + * Reports whether a folder is trusted according to the user's folder trust state. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture isTrusted } /** - * Folder path to add to trusted folders. + * Adds a folder to the user's trusted folders list. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApi.java index 46ce8ef4d3..37f16bf40c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsLocationsApi.java @@ -31,7 +31,7 @@ public final class SessionPermissionsLocationsApi { } /** - * Working directory to resolve into a location-permissions key. + * Resolves the permission location key and type for a working directory. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture resolve(Sessi } /** - * Working directory to load persisted location permissions for. + * Applies persisted location-scoped tool approvals and allowed directories for a working directory to this session's permission service. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -63,7 +63,7 @@ public CompletableFuture apply(SessionPe } /** - * Location-scoped tool approval to persist. + * Persists a tool approval for a permission location and applies its rules to this session's live permission service. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsApi.java index ecf9d81b52..62ae0a193b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsPathsApi.java @@ -31,7 +31,7 @@ public final class SessionPermissionsPathsApi { } /** - * No parameters; returns the session's recursive directory grants and exact session-approved paths. + * Returns the session's recursive directory grants, exact session-approved paths, and primary working directory. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture list() { } /** - * Directory path to add to the session's allowed directories. + * Adds a directory to the session's allow-list and activates conventional skill and agent definitions under it. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -58,7 +58,7 @@ public CompletableFuture add(SessionPermission } /** - * Directory path to set as the session's new primary working directory. + * Updates the session's primary working directory used by the permission policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -74,7 +74,7 @@ public CompletableFuture updatePrima } /** - * Path to evaluate against the session's allowed directories. + * Reports whether a path falls within any of the session's allowed directories. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -90,7 +90,7 @@ public CompletableFuture * Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsApi.java index 5ca15960d2..3261b07033 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPermissionsUrlsApi.java @@ -31,7 +31,7 @@ public final class SessionPermissionsUrlsApi { } /** - * Whether the URL-permission policy should run in unrestricted mode. + * Toggles the runtime's URL-permission policy between unrestricted and restricted modes. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanApi.java index 7805183ddf..daf85af7db 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPlanApi.java @@ -31,7 +31,7 @@ public final class SessionPlanApi { } /** - * Identifies the target session. + * Reads the session plan file from the workspace. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture read() { } /** - * Replacement contents to write to the session plan file. + * Writes new content to the session plan file. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -58,7 +58,7 @@ public CompletableFuture update(SessionPlanUpdateParams params) { } /** - * Identifies the target session. + * Deletes the session plan file from the workspace. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -69,7 +69,7 @@ public CompletableFuture delete() { } /** - * Identifies the target session. + * Reads todo rows from the session SQL database for plan rendering. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -80,7 +80,7 @@ public CompletableFuture readSqlTodos() { } /** - * Identifies the target session. + * Reads todo rows AND dependency edges from the session SQL database for structured progress UI. Same defensive behavior as readSqlTodos — returns empty arrays when the database, tables, or columns aren't available. Clients should call this on session start and after every `session.todos_changed` event to refresh structured-UI rendering. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java index 6a49785b30..3235108161 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsApi.java @@ -35,7 +35,7 @@ public final class SessionPluginsApi { } /** - * Identifies the target session. + * Lists globally installed, live, built-in, and enterprise-managed desired plugins using the live session's authoritative account, working directory, and retained managed policy. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -46,7 +46,7 @@ public CompletableFuture list() { } /** - * Plugin source resolved relative to the session's authoritative working directory. + * Installs a plugin using the live session's authoritative account, working directory, and retained managed policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -62,7 +62,7 @@ public CompletableFuture install(SessionPluginsInst } /** - * Name (or spec) of the plugin to uninstall. + * Uninstalls a plugin when permitted by the live session's retained managed policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -78,7 +78,7 @@ public CompletableFuture uninstall(SessionPluginsUninstallParams params) { } /** - * Name (or spec) of the plugin to update. + * Updates an installed plugin using the live session's authoritative account, working directory, and retained managed policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -94,7 +94,7 @@ public CompletableFuture update(SessionPluginsUpdate } /** - * Plugin names (or specs) to enable in the session's authoritative working directory. + * Enables installed plugins when permitted by the live session's retained managed policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -110,7 +110,7 @@ public CompletableFuture enable(SessionPluginsEnableParams params) { } /** - * Plugin names (or specs) to disable in the session's authoritative working directory. + * Disables installed plugins when permitted by the live session's retained managed policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -126,7 +126,7 @@ public CompletableFuture disable(SessionPluginsDisableParams params) { } /** - * Optional flags controlling which side effects the reload performs. + * Reloads the session's plugin set, refreshing MCP servers, custom agents, hooks, and skills cache so SDK-driven changes via `server.plugins.*` take effect immediately. *

* Invokes the method with no params, applying the runtime defaults. * @@ -139,7 +139,7 @@ public CompletableFuture reload() { } /** - * Optional flags controlling which side effects the reload performs. + * Reloads the session's plugin set, refreshing MCP servers, custom agents, hooks, and skills cache so SDK-driven changes via `server.plugins.*` take effect immediately. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -154,4 +154,33 @@ public CompletableFuture reload(SessionPluginsReloadParams params) { return caller.invoke("session.plugins.reload", _p, Void.class); } + /** + * Installs or updates the plugins the session's managed settings require now, without waiting for the next automatic attempt after a failure, then reloads plugins. Reports progress and failures with the same `session.info` and `session.warning` events as automatic preparation. Fails when the runtime does not prepare managed plugins for the session. + *

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

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture retryManaged(SessionPluginsRetryManagedParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = params == null ? MAPPER.createObjectNode() : MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.plugins.retryManaged", _p, SessionPluginsRetryManagedResult.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsMarketplacesApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsMarketplacesApi.java index 3ec14c5cf7..c9682c21f8 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsMarketplacesApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsMarketplacesApi.java @@ -31,7 +31,7 @@ public final class SessionPluginsMarketplacesApi { } /** - * Identifies the target session. + * Lists registered and enterprise-managed desired marketplaces using the live session's retained policy. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture list() { } /** - * Marketplace source and optional working directory for relative-path resolution. + * Adds a marketplace when permitted by the live session's retained managed policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -58,7 +58,7 @@ public CompletableFuture add(SessionPlugins } /** - * Name of the marketplace to remove and an optional force flag. + * Removes a marketplace when permitted by the live session's retained managed policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -74,7 +74,7 @@ public CompletableFuture remove(SessionP } /** - * Name of the marketplace whose plugin catalog to fetch. + * Browses a marketplace resolved through the live session's working directory and retained managed policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -90,7 +90,7 @@ public CompletableFuture browse(SessionP } /** - * Optional marketplace name; omit to refresh all. + * Refreshes marketplaces resolved through the live session's working directory and retained managed policy. *

* Invokes the method with no params, applying the runtime defaults. * @@ -103,7 +103,7 @@ public CompletableFuture refresh() { } /** - * Optional marketplace name; omit to refresh all. + * Refreshes marketplaces resolved through the live session's working directory and retained managed policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedParams.java new file mode 100644 index 0000000000..24b687ca75 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedParams.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Request parameters for the {@code session.plugins.retryManaged} RPC method. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionPluginsRetryManagedParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** "plugin@marketplace" specs to retry. Omit to retry every plugin the session's managed settings require. */ + @JsonProperty("plugins") List plugins +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedResult.java similarity index 72% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedResult.java index c5ab98621a..f2fc9b73cb 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsSetResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionPluginsRetryManagedResult.java @@ -15,7 +15,7 @@ import javax.annotation.processing.Generated; /** - * Outcome of writing user settings. + * Per-plugin outcomes of retrying the plugins required by the session's managed settings. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -24,8 +24,8 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record UserSettingsSetResult( - /** Top-level keys whose write landed in settings.json but is shadowed by a value still present in the legacy config.json (config.json wins on read). The write does not take effect until the legacy value is removed. */ - @JsonProperty("shadowedKeys") List shadowedKeys +public record SessionPluginsRetryManagedResult( + /** One entry per requested plugin, in request order. When no plugins were requested, one entry per plugin the managed settings require. */ + @JsonProperty("plugins") List plugins ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderApi.java index 26ebee8489..8d3b283405 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProviderApi.java @@ -31,7 +31,7 @@ public final class SessionProviderApi { } /** - * Optional model identifier to scope the endpoint snapshot to. + * Returns the provider endpoint and credentials the session is currently configured to talk to, so the caller can make inference calls directly against the same backend the session uses. *

* Invokes the method with no params, applying the runtime defaults. * @@ -44,7 +44,7 @@ public CompletableFuture getEndpoint() { } /** - * Optional model identifier to scope the endpoint snapshot to. + * Returns the provider endpoint and credentials the session is currently configured to talk to, so the caller can make inference calls directly against the same backend the session uses. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -60,7 +60,7 @@ public CompletableFuture getEndpoint(SessionPr } /** - * BYOK providers and/or models to add to the session's registry at runtime. Both fields are optional; provide providers, models, or both. + * Adds BYOK providers and/or models to the session's registry at runtime, extending the additive registry built from the session's `providers`/`models` options. Both fields are optional, so a call may add providers only, models only, or both. Within a single call providers are registered before models, so a model may reference a provider added in the same call; across calls a model may reference any provider already registered (from session creation or a prior add). A model whose referenced provider is not registered by the end of the call is rejected. Newly added models become selectable via `model.list` / `model.switchTo` and are inherited by sub-agents spawned afterwards. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -76,7 +76,7 @@ public CompletableFuture add(SessionProviderAddParams } /** - * Authoritative BYOK provider and model registry snapshot to apply atomically to the session. + * Atomically updates the session's BYOK provider and model registry by applying the supplied snapshot, replacing existing entries, updating models, or removing entries absent from the snapshot. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -92,7 +92,7 @@ public CompletableFuture sync(SessionProviderSyncPara } /** - * Host-managed model selection ids to withdraw from the session's BYOK registry. + * Withdraws named host-managed models from the session's BYOK registry, leaving every other entry untouched. The scoped counterpart to `provider.sync`: a snapshot can only describe entries the caller knows about, so using it to remove one model silently withdraws rows registered by another source, such as a plugin calling `provider.add` at runtime. Naming what to remove leaves unrelated entries alone. Selection ids that are not registered are ignored, so withdrawal is idempotent. A provider is removed only when one of the withdrawn models was the last entry referencing it; a provider that simply has no models, which is the normal state while its rows are supplied by catalog discovery, is left in place. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersApi.java new file mode 100644 index 0000000000..f7dde45999 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersApi.java @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 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 providers} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionProvidersApi { + + private static final com.fasterxml.jackson.databind.ObjectMapper MAPPER = RpcMapper.INSTANCE; + + private final RpcCaller caller; + private final String sessionId; + + /** API methods for the {@code providers.models} sub-namespace. */ + public final SessionProvidersModelsApi models; + + /** @param caller the RPC transport function */ + SessionProvidersApi(RpcCaller caller, String sessionId) { + this.caller = caller; + this.sessionId = sessionId; + this.models = new SessionProvidersModelsApi(caller, sessionId); + } + + /** + * Returns adapter definitions and supported operations in this session's effective provider catalog, without running discovery. Does not list provider instances or select inference models. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture getCatalog() { + return caller.invoke("session.providers.getCatalog", java.util.Map.of("sessionId", this.sessionId), SessionProvidersGetCatalogResult.class); + } + + /** + * Discovers reachable instances using an adapter from this session's effective provider catalog and provider-specific discovery input. + *

+ * 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 discover(SessionProvidersDiscoverParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.providers.discover", _p, SessionProvidersDiscoverResult.class); + } + + /** + * Gets current health and version information for a discovered model-provider instance. + *

+ * 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 getStatus(SessionProvidersGetStatusParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.providers.getStatus", _p, SessionProvidersGetStatusResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersDiscoverParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersDiscoverParams.java new file mode 100644 index 0000000000..f5400285f8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersDiscoverParams.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; + +/** + * Provider discovery parameters. + * + * @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 SessionProvidersDiscoverParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Opaque adapter identity returned by `session.providers.getCatalog`. */ + @JsonProperty("adapterId") String adapterId, + /** 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. */ + @JsonProperty("input") Object input +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersDiscoverResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersDiscoverResult.java new file mode 100644 index 0000000000..1467adc6a9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersDiscoverResult.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; + +/** + * Provider instances found by a discovery operation. + * + * @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 SessionProvidersDiscoverResult( + /** Typed operation outcome. Passive discovery can return `absent` with an empty instance list. */ + @JsonProperty("outcome") ModelProviderOperationOutcome outcome, + /** Discovered provider instances. Empty when passive default discovery finds no reachable provider. */ + @JsonProperty("instances") List instances +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetCatalogParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetCatalogParams.java new file mode 100644 index 0000000000..1fd343bb0a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetCatalogParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Identifies the target session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionProvidersGetCatalogParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetCatalogResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetCatalogResult.java new file mode 100644 index 0000000000..49c23ba195 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetCatalogResult.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; + +/** + * Normalized model-provider adapter definitions available to the session, not discovered instances. + * + * @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 SessionProvidersGetCatalogResult( + /** Available provider adapters ordered by adapterId. */ + @JsonProperty("providers") List providers +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetStatusParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetStatusParams.java new file mode 100644 index 0000000000..56e686dd30 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetStatusParams.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; + +/** + * Provider status request parameters. + * + * @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 SessionProvidersGetStatusParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Provider instance reference returned by discovery. */ + @JsonProperty("instance") ModelProviderInstanceReference instance +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetStatusResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetStatusResult.java new file mode 100644 index 0000000000..cff3c86ed8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersGetStatusResult.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; + +/** + * Current health information for a provider instance. + * + * @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 SessionProvidersGetStatusResult( + /** Typed operation outcome. */ + @JsonProperty("outcome") ModelProviderOperationOutcome outcome, + /** Normalized provider instance. */ + @JsonProperty("instance") ModelProviderInstance instance, + /** Open provider status value, such as `healthy`, `unreachable`, or `notInstalled`. */ + @JsonProperty("status") String status, + /** Provider-reported version. */ + @JsonProperty("version") String version +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsApi.java new file mode 100644 index 0000000000..3f5855d9a8 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsApi.java @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 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 providers.models} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionProvidersModelsApi { + + private static final com.fasterxml.jackson.databind.ObjectMapper MAPPER = RpcMapper.INSTANCE; + + private final RpcCaller caller; + private final String sessionId; + + /** @param caller the RPC transport function */ + SessionProvidersModelsApi(RpcCaller caller, String sessionId) { + this.caller = caller; + this.sessionId = sessionId; + } + + /** + * Lists models installed or otherwise available from a discovered model-provider instance. + *

+ * 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 list(SessionProvidersModelsListParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.providers.models.list", _p, SessionProvidersModelsListResult.class); + } + + /** + * Translates a discovered model into the provider and model configuration needed to use it, and reports whether each is already registered in this session. Prepares only: it registers nothing, writes nothing, and performs no provider requests. + *

+ * 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 prepareConfiguration(SessionProvidersModelsPrepareConfigurationParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.providers.models.prepareConfiguration", _p, SessionProvidersModelsPrepareConfigurationResult.class); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsListParams.java new file mode 100644 index 0000000000..89f4984ae4 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsListParams.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; + +/** + * Provider model inventory request parameters. + * + * @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 SessionProvidersModelsListParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Provider instance reference returned by discovery. */ + @JsonProperty("instance") ModelProviderInstanceReference instance +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsListResult.java new file mode 100644 index 0000000000..307ee6728b --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsListResult.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; + +/** + * 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. + * + * @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 SessionProvidersModelsListResult( + /** Typed operation outcome. */ + @JsonProperty("outcome") ModelProviderOperationOutcome outcome, + /** Provider-native models in provider order. */ + @JsonProperty("models") List models +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsPrepareConfigurationParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsPrepareConfigurationParams.java new file mode 100644 index 0000000000..60548516cb --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsPrepareConfigurationParams.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 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`. + * + * @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 SessionProvidersModelsPrepareConfigurationParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** The discovered instance that serves the model. */ + @JsonProperty("instance") ModelProviderInstance instance, + /** The discovered model to configure. */ + @JsonProperty("model") DiscoveredModel model +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsPrepareConfigurationResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsPrepareConfigurationResult.java new file mode 100644 index 0000000000..e2f2abeebb --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionProvidersModelsPrepareConfigurationResult.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; + +/** + * 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. + * + * @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 SessionProvidersModelsPrepareConfigurationResult( + /** Provider connection prepared from the instance's inference metadata. Carries no credential; supply one if the endpoint requires it. */ + @JsonProperty("provider") NamedProviderConfig provider, + /** Model definition prepared from the discovered model. Capability fields the provider did not report stay omitted rather than being asserted false. */ + @JsonProperty("model") ProviderModelConfig model, + /** Provider-qualified selection id (`provider/id`) to pass to `switchTo` once the plan is applied. */ + @JsonProperty("selectionId") String selectionId, + /** 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. */ + @JsonProperty("providerDisposition") ModelProviderConfigurationDisposition providerDisposition, + /** Whether `model` still needs to be registered. When `alreadyConfigured`, `selectionId` is already registered and the caller can select it without adding anything. */ + @JsonProperty("modelDisposition") ModelProviderConfigurationDisposition modelDisposition, + /** Non-fatal warnings carried over from the discovered model, such as capabilities the provider did not report. */ + @JsonProperty("warnings") List warnings +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueApi.java index 5e6d67690c..e38110708f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueApi.java @@ -31,7 +31,7 @@ public final class SessionQueueApi { } /** - * Identifies the target session. + * Returns the local session's pending user-facing queued items and steering messages. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,18 +42,18 @@ public CompletableFuture pendingItems() { } /** - * Identifies the target session. + * Returns the internal native queue snapshot for in-process session orchestration. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture snapshot() { - return caller.invoke("session.queue.snapshot", java.util.Map.of("sessionId", this.sessionId), SessionQueueSnapshotResult.class); + CompletableFuture snapshot() { + return caller.invoke("session.queue.snapshot", java.util.Map.of("sessionId", this.sessionId), QueueSnapshotResult.class); } /** - * Parameters for moving a queued item by stable id. + * Moves an addressable queued item to a public visible position. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -69,7 +69,7 @@ public CompletableFuture moveItem(SessionQueueMoveIt } /** - * Parameters for inserting a queued message at a public visible position. + * Inserts a new queued message at a public visible position. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -85,7 +85,7 @@ public CompletableFuture insertAt(SessionQueueInsert } /** - * Parameters for removing a queued item by stable id. + * Removes an addressable queued item by its stable id. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -101,7 +101,7 @@ public CompletableFuture removeAt(SessionQueueRemove } /** - * Parameters for editing a single queued message. + * Updates the text of an addressable single-message queue item. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -117,7 +117,7 @@ public CompletableFuture updateText(SessionQueueUp } /** - * Conditional withdrawal of a single user message, from its queue or from the running turn it started. + * Atomically withdraws an unchanged user message of a local session: from the queued or steering lane while unconsumed, or from the running turn it started while the model has not answered it and nothing the user sent after it is pending. Withdrawing from the running turn interrupts that turn and removes its events from history. A client retaining the original draft may restore it only when removed is true. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -133,7 +133,7 @@ public CompletableFuture withdrawMessage(Sess } /** - * Append to one pending steering message without changing its identity or delivery position. + * Atomically appends text and attachments to an unchanged, unconsumed local steering message. Returns updated=false if delivery or withdrawal already claimed the message. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -149,7 +149,7 @@ public CompletableFuture appendSteering(Sessio } /** - * Parameters for duplicating a queued item. + * Duplicates an addressable queued item immediately after its source. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -165,7 +165,7 @@ public CompletableFuture duplicateAt(SessionQueue } /** - * 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. + * Acquires or releases the queued-lane drain pause. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -181,7 +181,7 @@ public CompletableFuture setDrainPaused(SessionQueueSetDrainPausedParams p } /** - * Parameters for steering a queued message into a live turn. + * Moves an addressable queued message into the live turn's steering lane. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -197,18 +197,18 @@ public CompletableFuture sendNow(SessionQueueSendNowP } /** - * Identifies the target session. + * Reports whether the local session has native queued work pending. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture hasPending() { - return caller.invoke("session.queue.hasPending", java.util.Map.of("sessionId", this.sessionId), SessionQueueHasPendingResult.class); + CompletableFuture hasPending() { + return caller.invoke("session.queue.hasPending", java.util.Map.of("sessionId", this.sessionId), QueueHasPendingResult.class); } /** - * Inputs for starting a deferred-idle drain. + * Begins a native deferred-idle drain when background work has quiesced. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -217,14 +217,14 @@ public CompletableFuture hasPending() { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture beginDeferredIdleDrain(SessionQueueBeginDeferredIdleDrainParams params) { + CompletableFuture beginDeferredIdleDrain(SessionQueueBeginDeferredIdleDrainParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.queue.beginDeferredIdleDrain", _p, SessionQueueBeginDeferredIdleDrainResult.class); + return caller.invoke("session.queue.beginDeferredIdleDrain", _p, QueueBeginDeferredIdleDrainResult.class); } /** - * Inputs for completing a deferred-idle drain. + * Finishes a native deferred-idle drain and reports whether to drain queue work or emit idle. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -233,14 +233,14 @@ public CompletableFuture beginDeferred * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture finishDeferredIdleDrain(SessionQueueFinishDeferredIdleDrainParams params) { + CompletableFuture finishDeferredIdleDrain(SessionQueueFinishDeferredIdleDrainParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.queue.finishDeferredIdleDrain", _p, SessionQueueFinishDeferredIdleDrainResult.class); + return caller.invoke("session.queue.finishDeferredIdleDrain", _p, QueueFinishDeferredIdleDrainResult.class); } /** - * Inputs for marking session.idle deferred in native state. + * Marks session.idle as deferred by native background work state. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -249,14 +249,14 @@ public CompletableFuture finishDeferr * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture deferSessionIdle(SessionQueueDeferSessionIdleParams params) { + CompletableFuture deferSessionIdle(SessionQueueDeferSessionIdleParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); return caller.invoke("session.queue.deferSessionIdle", _p, Void.class); } /** - * Identifies the target session. + * Removes the most recently queued user-facing item (LIFO). * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -267,7 +267,7 @@ public CompletableFuture removeMostRecent() } /** - * Identifies the target session. + * Clears all pending queued items on the local session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -278,7 +278,7 @@ public CompletableFuture clear() { } /** - * Internal filter for consuming queued system notifications. + * Consumes queued native system notifications matching an internal filter. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -287,31 +287,31 @@ public CompletableFuture clear() { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture consumeSystemNotifications(SessionQueueConsumeSystemNotificationsParams params) { + CompletableFuture consumeSystemNotifications(SessionQueueConsumeSystemNotificationsParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.queue.consumeSystemNotifications", _p, SessionQueueConsumeSystemNotificationsResult.class); + return caller.invoke("session.queue.consumeSystemNotifications", _p, QueueRemoveMostRecentResult.class); } /** - * Identifies the target session. + * Enqueues the internal resume-pending wake item when orphan handling needs a follow-up turn. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture enqueueResumePending() { - return caller.invoke("session.queue.enqueueResumePending", java.util.Map.of("sessionId", this.sessionId), SessionQueueEnqueueResumePendingResult.class); + CompletableFuture enqueueResumePending() { + return caller.invoke("session.queue.enqueueResumePending", java.util.Map.of("sessionId", this.sessionId), QueueEnqueueResumePendingResult.class); } /** - * Identifies the target session. + * Drains the native local-session work queue for in-process session orchestration. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture process() { + CompletableFuture process() { return caller.invoke("session.queue.process", java.util.Map.of("sessionId", this.sessionId), Void.class); } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainParams.java index 4973e31075..a8e3790e7e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueBeginDeferredIdleDrainParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueBeginDeferredIdleDrainParams( +record SessionQueueBeginDeferredIdleDrainParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Whether the host still has active background work. */ 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 6449ce7666..3b1d844881 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 @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueConsumeSystemNotificationsParams( +record SessionQueueConsumeSystemNotificationsParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Opaque runtime-owned filter object. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDeferSessionIdleParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDeferSessionIdleParams.java index 7b3dff9ef5..ec96cf762e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDeferSessionIdleParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueDeferSessionIdleParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueDeferSessionIdleParams( +record SessionQueueDeferSessionIdleParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Whether the deferred idle was caused by an aborted foreground turn. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingParams.java index 1a0ec546ae..6817219452 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueEnqueueResumePendingParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueEnqueueResumePendingParams( +record SessionQueueEnqueueResumePendingParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainParams.java index b6b29057d8..43568d4cd9 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueFinishDeferredIdleDrainParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueFinishDeferredIdleDrainParams( +record SessionQueueFinishDeferredIdleDrainParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Whether the host still has active background work. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingParams.java index e587fec4cb..4b8ef92b5f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueHasPendingParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueHasPendingParams( +record SessionQueueHasPendingParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueProcessParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueProcessParams.java index 8c8edfe554..76c805ac4c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueProcessParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueProcessParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueProcessParams( +record SessionQueueProcessParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotParams.java index dff5db8a8d..7963b017c4 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQueueSnapshotParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionQueueSnapshotParams( +record SessionQueueSnapshotParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaApi.java new file mode 100644 index 0000000000..15ecde4f84 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaApi.java @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) 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.List; +import java.util.concurrent.CompletableFuture; +import javax.annotation.processing.Generated; + +/** + * API methods for the {@code quota} namespace. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionQuotaApi { + + private final RpcCaller caller; + private final String sessionId; + + /** @param caller the RPC transport function */ + SessionQuotaApi(RpcCaller caller, String sessionId) { + this.caller = caller; + this.sessionId = sessionId; + } + + /** + * Gets the session's current quota and account projection without making a network request. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture get() { + return caller.invoke("session.quota.get", java.util.Map.of("sessionId", this.sessionId), SessionQuotaGetResult.class); + } + + /** + * Refreshes the session's provider model catalog bypassing its cache, folds quota snapshots into session state, and returns the updated projection. Failures leave the last known quota intact. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture refresh() { + return caller.invoke("session.quota.refresh", java.util.Map.of("sessionId", this.sessionId), SessionQuotaRefreshResult.class); + } + + /** + * Returns and clears the session's pending quota warnings. Reading or refreshing quota does not drain warnings. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture> takeWarnings() { + return caller.invoke("session.quota.takeWarnings", java.util.Map.of("sessionId", this.sessionId), RpcMapper.INSTANCE.getTypeFactory().constructCollectionType(List.class, QuotaWarningProjection.class)); + } + +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaDelegateWarning.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaDelegateWarning.java new file mode 100644 index 0000000000..ed6331a96f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaDelegateWarning.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; + +/** + * Existing delegation guidance for an account without delegation access. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionQuotaDelegateWarning( + /** User-facing guidance. */ + @JsonProperty("text") String text, + /** Link to upgrade the account. */ + @JsonProperty("url") String url +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaGetParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaGetParams.java new file mode 100644 index 0000000000..1c66851a3d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaGetParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Identifies the target session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionQuotaGetParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaGetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaGetResult.java new file mode 100644 index 0000000000..cc92c0199f --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaGetResult.java @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All 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; + +/** + * The incumbent session-owned quota and account projection. + * + * @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 SessionQuotaGetResult( + /** Known quota snapshots, keyed by the provider's quota type. */ + @JsonProperty("snapshots") Map snapshots, + /** Provider-owned account quota state, including snapshots without numeric balances. */ + @JsonProperty("providerQuotas") List providerQuotas, + /** Whether the account uses the free limited Copilot plan. */ + @JsonProperty("isFreeUser") Boolean isFreeUser, + /** Whether the account uses token-based billing. */ + @JsonProperty("isTbbUser") Boolean isTbbUser, + /** Resolved Copilot plan tier. */ + @JsonProperty("planTier") SessionQuotaPlanTier planTier, + /** Whether premium-request costs are billable. */ + @JsonProperty("premiumRequestsBillable") Boolean premiumRequestsBillable, + /** Whether model cost columns should be shown. */ + @JsonProperty("modelCostColumnVisible") Boolean modelCostColumnVisible, + /** Whether the account can delegate tasks to GitHub. */ + @JsonProperty("delegateAvailable") Boolean delegateAvailable, + /** Whether the account can sign up for Copilot Free. */ + @JsonProperty("canSignupForCopilotFree") Boolean canSignupForCopilotFree, + /** Whether dynamic workflows are enabled for the active account. */ + @JsonProperty("dynamicWorkflowsEnabled") Boolean dynamicWorkflowsEnabled, + /** Whether dynamic workflows are visible under the session's feature flags. */ + @JsonProperty("dynamicWorkflowsUiVisible") Boolean dynamicWorkflowsUiVisible, + /** Upgrade link for a free account. */ + @JsonProperty("upgradeUrl") String upgradeUrl, + /** Existing delegation warning for a free account. */ + @JsonProperty("delegateWarning") SessionQuotaDelegateWarning delegateWarning +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaPlanTier.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaPlanTier.java new file mode 100644 index 0000000000..fc2e5c53c9 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaPlanTier.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; + +/** + * Copilot plan tier used by the session quota projection. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum SessionQuotaPlanTier { + /** The {@code free} variant. */ + FREE("free"), + /** The {@code edu} variant. */ + EDU("edu"), + /** The {@code pro} variant. */ + PRO("pro"), + /** The {@code pro_plus} variant. */ + PRO_PLUS("pro_plus"), + /** The {@code business} variant. */ + BUSINESS("business"), + /** The {@code enterprise} variant. */ + ENTERPRISE("enterprise"), + /** The {@code max} variant. */ + MAX("max"), + /** The {@code unknown} variant. */ + UNKNOWN("unknown"); + + private final String value; + SessionQuotaPlanTier(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static SessionQuotaPlanTier fromValue(String value) { + for (SessionQuotaPlanTier v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown SessionQuotaPlanTier value: " + value); + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaRefreshParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaRefreshParams.java new file mode 100644 index 0000000000..ac51e99123 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaRefreshParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Identifies the target session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionQuotaRefreshParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaRefreshResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaRefreshResult.java new file mode 100644 index 0000000000..0f0892d284 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaRefreshResult.java @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All 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; + +/** + * The incumbent session-owned quota and account projection. + * + * @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 SessionQuotaRefreshResult( + /** Known quota snapshots, keyed by the provider's quota type. */ + @JsonProperty("snapshots") Map snapshots, + /** Provider-owned account quota state, including snapshots without numeric balances. */ + @JsonProperty("providerQuotas") List providerQuotas, + /** Whether the account uses the free limited Copilot plan. */ + @JsonProperty("isFreeUser") Boolean isFreeUser, + /** Whether the account uses token-based billing. */ + @JsonProperty("isTbbUser") Boolean isTbbUser, + /** Resolved Copilot plan tier. */ + @JsonProperty("planTier") SessionQuotaPlanTier planTier, + /** Whether premium-request costs are billable. */ + @JsonProperty("premiumRequestsBillable") Boolean premiumRequestsBillable, + /** Whether model cost columns should be shown. */ + @JsonProperty("modelCostColumnVisible") Boolean modelCostColumnVisible, + /** Whether the account can delegate tasks to GitHub. */ + @JsonProperty("delegateAvailable") Boolean delegateAvailable, + /** Whether the account can sign up for Copilot Free. */ + @JsonProperty("canSignupForCopilotFree") Boolean canSignupForCopilotFree, + /** Whether dynamic workflows are enabled for the active account. */ + @JsonProperty("dynamicWorkflowsEnabled") Boolean dynamicWorkflowsEnabled, + /** Whether dynamic workflows are visible under the session's feature flags. */ + @JsonProperty("dynamicWorkflowsUiVisible") Boolean dynamicWorkflowsUiVisible, + /** Upgrade link for a free account. */ + @JsonProperty("upgradeUrl") String upgradeUrl, + /** Existing delegation warning for a free account. */ + @JsonProperty("delegateWarning") SessionQuotaDelegateWarning delegateWarning +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaSnapshot.java new file mode 100644 index 0000000000..996bdf7227 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaSnapshot.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 com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Session quota snapshot, preserving the incumbent numeric units and reset metadata. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionQuotaSnapshot( + /** Whether the entitlement is unlimited. */ + @JsonProperty("isUnlimitedEntitlement") Boolean isUnlimitedEntitlement, + /** Entitled quantity in this quota's units. */ + @JsonProperty("entitlementRequests") Double entitlementRequests, + /** Consumed quantity in this quota's units. */ + @JsonProperty("usedRequests") Double usedRequests, + /** Whether usage is allowed after quota exhaustion. */ + @JsonProperty("usageAllowedWithExhaustedQuota") Boolean usageAllowedWithExhaustedQuota, + /** Additional usage in this quota's units. */ + @JsonProperty("overage") Double overage, + /** Whether additional usage is allowed after quota exhaustion. */ + @JsonProperty("overageAllowedWithExhaustedQuota") Boolean overageAllowedWithExhaustedQuota, + /** Percentage of the entitlement remaining. */ + @JsonProperty("remainingPercentage") Double remainingPercentage, + /** Quota reset time in milliseconds since the Unix epoch, when known. */ + @JsonProperty("resetDateEpochMs") Double resetDateEpochMs, + /** Whether the reset time is estimated. */ + @JsonProperty("resetDateEstimated") Boolean resetDateEstimated, + /** Whether the provider reports available quota. */ + @JsonProperty("hasQuota") Boolean hasQuota, + /** Whether this quota uses token-based billing. */ + @JsonProperty("tokenBasedBilling") Boolean tokenBasedBilling, + /** Additional-usage budget cap, when provided. */ + @JsonProperty("overageEntitlement") Double overageEntitlement +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaTakeWarningsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaTakeWarningsParams.java new file mode 100644 index 0000000000..2a96fdd5a2 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionQuotaTakeWarningsParams.java @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; +import javax.annotation.processing.Generated; + +/** + * Identifies the target session. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionQuotaTakeWarningsParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteApi.java index bb1cfa0e25..015c69f830 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionRemoteApi.java @@ -31,7 +31,7 @@ public final class SessionRemoteApi { } /** - * Optional remote session mode ("off", "export", or "on"); defaults to enabling both export and remote steering. + * Enables remote session export or steering. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture enable(SessionRemoteEnablePa } /** - * Identifies the target session. + * Disables remote session export and steering. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -58,7 +58,7 @@ public CompletableFuture disable() { } /** - * New remote-steerability state to persist as a `session.remote_steerable_changed` event. + * Persists a remote-steerability change emitted by the host as a session event. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. 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 bb3bb0680b..21050a5317 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 @@ -29,6 +29,8 @@ public final class SessionRpc { private final RpcCaller caller; private final String sessionId; + /** API methods for the {@code providers} namespace. */ + public final SessionProvidersApi providers; /** API methods for the {@code sandbox} namespace. */ public final SessionSandboxApi sandbox; /** API methods for the {@code gitHubAuth} namespace. */ @@ -98,7 +100,7 @@ public final class SessionRpc { /** API methods for the {@code metadata} namespace. */ public final SessionMetadataApi metadata; /** API methods for the {@code settings} namespace. */ - public final SessionSettingsApi settings; + final SessionSettingsApi settings; /** API methods for the {@code contentExclusion} namespace. */ public final SessionContentExclusionApi contentExclusion; /** API methods for the {@code shell} namespace. */ @@ -111,6 +113,8 @@ public final class SessionRpc { public final SessionEventLogApi eventLog; /** API methods for the {@code usage} namespace. */ public final SessionUsageApi usage; + /** API methods for the {@code quota} namespace. */ + public final SessionQuotaApi quota; /** API methods for the {@code limitPrediction} namespace. */ public final SessionLimitPredictionApi limitPrediction; /** API methods for the {@code remote} namespace. */ @@ -129,6 +133,7 @@ public final class SessionRpc { public SessionRpc(RpcCaller caller, String sessionId) { this.caller = caller; this.sessionId = sessionId; + this.providers = new SessionProvidersApi(caller, sessionId); this.sandbox = new SessionSandboxApi(caller, sessionId); this.gitHubAuth = new SessionGitHubAuthApi(caller, sessionId); this.accounts = new SessionAccountsApi(caller, sessionId); @@ -170,6 +175,7 @@ public SessionRpc(RpcCaller caller, String sessionId) { this.queue = new SessionQueueApi(caller, sessionId); this.eventLog = new SessionEventLogApi(caller, sessionId); this.usage = new SessionUsageApi(caller, sessionId); + this.quota = new SessionQuotaApi(caller, sessionId); this.limitPrediction = new SessionLimitPredictionApi(caller, sessionId); this.remote = new SessionRemoteApi(caller, sessionId); this.visibility = new SessionVisibilityApi(caller, sessionId); @@ -177,7 +183,7 @@ public SessionRpc(RpcCaller caller, String sessionId) { } /** - * Identifies the target session. + * Suspends the session while preserving persisted state for later resume. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -188,7 +194,7 @@ public CompletableFuture suspend() { } /** - * Parameters for sending a user message to the session + * Sends a user message to the session and returns its message ID. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -204,7 +210,7 @@ public CompletableFuture send(SessionSendParams params) { } /** - * 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. + * Sends zero or more user messages to the session in a single turn and returns their message IDs. All provided messages are appended to the conversation in order, then exactly one agent turn runs over the resulting history. When the list is empty, one turn runs over the existing history with no new user message. Remote-backed (Mission Control) sessions do not support this method and will return an error. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -220,7 +226,7 @@ public CompletableFuture sendMessages(SessionSendMess } /** - * Internal request for sending a system notification. + * Queues or sends an internal system notification to the session according to its passive policy. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -229,14 +235,14 @@ public CompletableFuture sendMessages(SessionSendMess * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture sendSystemNotification(SessionSendSystemNotificationParams params) { + CompletableFuture sendSystemNotification(SessionSendSystemNotificationParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); return caller.invoke("session.sendSystemNotification", _p, Void.class); } /** - * Parameters for aborting the current turn + * Aborts the current agent turn. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -252,7 +258,7 @@ public CompletableFuture abort(SessionAbortParams params) { } /** - * Parameters for interrupting the main agent turn. + * Interrupts the current main agent turn while leaving running background work (subagents, sidekicks, and promoted attached shells) alive. No-op when the main loop is not processing. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -268,7 +274,7 @@ public CompletableFuture interruptMainTurn(Sessi } /** - * Identifies the target session. + * Cancels every running background agent (task-registry subagents plus sidekick agents) without interrupting the main agent loop. Promoted attached shells are left running. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -279,7 +285,7 @@ public CompletableFuture cancelAllBackgroundAgents() { } /** - * Parameters for shutting down the session + * Shuts down the session and persists its final state. Awaits any deferred sessionEnd hooks before resolving so user-supplied hook scripts complete before the runtime tears down. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -295,7 +301,7 @@ public CompletableFuture shutdown(SessionShutdownParams params) { } /** - * Message text, optional severity level, persistence flag, optional follow-up URL, and optional tip. + * Emits a user-visible session log event. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxApi.java index a13ef559ef..2ace81bb8e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSandboxApi.java @@ -31,7 +31,7 @@ public final class SessionSandboxApi { } /** - * Identifies the target session. + * Returns whether managed policy requires sandbox enforcement and whether an enforcement failure has permanently blocked the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture getEnforcemen } /** - * Request to disable sandboxing for the current session while resolving an active sandbox-bypass permission prompt. + * Disables sandboxing for the remainder of the current session and approves the referenced pending sandbox-bypass permission request. The request is rejected unless the exact request is still pending and the effective sandbox policy permits bypass. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -58,7 +58,7 @@ public CompletableFuture disableForSessio } /** - * Request to accept the sandbox path grant offered on an active sandbox escalation permission prompt. + * Adds the path offered by a pending sandbox escalation permission request's sandboxPathGrant to the session's sandbox policy and approves the request, so the blocked operation re-runs inside the sandbox rather than outside it. The request is rejected unless the exact request is still pending, carries a sandboxPathGrant, and the grant still takes effect under the current managed policy. Does not persist the path; hosts that store sandbox settings save it themselves. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtParams.java index 0a099bdf4a..11a8ac65d2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddAtParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionScheduleAddAtParams( +record SessionScheduleAddAtParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Epoch milliseconds when the prompt should fire. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronParams.java index 08a9cd33bb..732fe8b343 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddCronParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionScheduleAddCronParams( +record SessionScheduleAddCronParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** 5-field cron expression. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddParams.java index 31580758cb..b388ce49db 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionScheduleAddParams( +record SessionScheduleAddParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Human-readable interval such as `30s`, `5m`, or `2h`. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedParams.java index 17a89c1b2d..f2fa5e2860 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleAddSelfPacedParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionScheduleAddSelfPacedParams( +record SessionScheduleAddSelfPacedParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Prompt text to enqueue when the schedule fires. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleApi.java index f983f84f71..0b6a90aca9 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleApi.java @@ -31,7 +31,7 @@ public final class SessionScheduleApi { } /** - * Identifies the target session. + * Lists the session's currently active scheduled prompts. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,29 +42,29 @@ public CompletableFuture list() { } /** - * Identifies the target session. + * Hydrates the native schedule registry from persisted session events. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture hydrate() { + CompletableFuture hydrate() { return caller.invoke("session.schedule.hydrate", java.util.Map.of("sessionId", this.sessionId), Void.class); } /** - * Identifies the target session. + * Reports whether the session has an active self-paced scheduled prompt. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture hasSelfPaced() { - return caller.invoke("session.schedule.hasSelfPaced", java.util.Map.of("sessionId", this.sessionId), SessionScheduleHasSelfPacedResult.class); + CompletableFuture hasSelfPaced() { + return caller.invoke("session.schedule.hasSelfPaced", java.util.Map.of("sessionId", this.sessionId), ScheduleHasSelfPacedResult.class); } /** - * Register a relative-interval scheduled prompt. + * Registers a relative-interval scheduled prompt. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -73,14 +73,14 @@ public CompletableFuture hasSelfPaced() { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture add(SessionScheduleAddParams params) { + CompletableFuture add(SessionScheduleAddParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.schedule.add", _p, SessionScheduleAddResult.class); + return caller.invoke("session.schedule.add", _p, ScheduleAddResult.class); } /** - * Register a cron scheduled prompt. + * Registers a recurring cron scheduled prompt. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -89,14 +89,14 @@ public CompletableFuture add(SessionScheduleAddParams * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture addCron(SessionScheduleAddCronParams params) { + CompletableFuture addCron(SessionScheduleAddCronParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.schedule.addCron", _p, SessionScheduleAddCronResult.class); + return caller.invoke("session.schedule.addCron", _p, ScheduleAddResult.class); } /** - * Register an absolute-time scheduled prompt. + * Registers an absolute-time scheduled prompt. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -105,14 +105,14 @@ public CompletableFuture addCron(SessionScheduleAd * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture addAt(SessionScheduleAddAtParams params) { + CompletableFuture addAt(SessionScheduleAddAtParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.schedule.addAt", _p, SessionScheduleAddAtResult.class); + return caller.invoke("session.schedule.addAt", _p, ScheduleAddResult.class); } /** - * Register a self-paced scheduled prompt. + * Registers a self-paced scheduled prompt. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -121,14 +121,14 @@ public CompletableFuture addAt(SessionScheduleAddAtP * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture addSelfPaced(SessionScheduleAddSelfPacedParams params) { + CompletableFuture addSelfPaced(SessionScheduleAddSelfPacedParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.schedule.addSelfPaced", _p, SessionScheduleAddSelfPacedResult.class); + return caller.invoke("session.schedule.addSelfPaced", _p, ScheduleAddResult.class); } /** - * Re-arm a self-paced scheduled prompt. + * Re-arms an active self-paced scheduled prompt. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -137,14 +137,14 @@ public CompletableFuture addSelfPaced(Session * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture rearmSelfPaced(SessionScheduleRearmSelfPacedParams params) { + CompletableFuture rearmSelfPaced(SessionScheduleRearmSelfPacedParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.schedule.rearmSelfPaced", _p, SessionScheduleRearmSelfPacedResult.class); + return caller.invoke("session.schedule.rearmSelfPaced", _p, ScheduleAddResult.class); } /** - * Identifier of the scheduled prompt to remove. + * Removes a scheduled prompt by id. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedParams.java index 7eb31df7f6..134a381e3b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHasSelfPacedParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionScheduleHasSelfPacedParams( +record SessionScheduleHasSelfPacedParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHydrateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHydrateParams.java index 32ec85c7d4..6eda2a1b32 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHydrateParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleHydrateParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionScheduleHydrateParams( +record SessionScheduleHydrateParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedParams.java index d1999311c2..f633a98626 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionScheduleRearmSelfPacedParams( +record SessionScheduleRearmSelfPacedParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Id of the self-paced scheduled prompt. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedResult.java deleted file mode 100644 index 0280fbaccb..0000000000 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionScheduleRearmSelfPacedResult.java +++ /dev/null @@ -1,32 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All 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; - -/** - * Result of registering or re-arming a scheduled prompt. - * - * @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 SessionScheduleRearmSelfPacedResult( - /** The registered or updated schedule entry. */ - @JsonProperty("entry") ScheduleEntry entry, - /** User-facing validation error, when registration failed. */ - @JsonProperty("error") String error -) { -} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendSystemNotificationParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendSystemNotificationParams.java index 762fe6ef39..dbb8120345 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendSystemNotificationParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSendSystemNotificationParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSendSystemNotificationParams( +record SessionSendSystemNotificationParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Notification text to deliver to the model. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsApi.java index dd4acfdb53..f07d028f93 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsApi.java @@ -17,7 +17,7 @@ * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public final class SessionSettingsApi { +final class SessionSettingsApi { private static final com.fasterxml.jackson.databind.ObjectMapper MAPPER = RpcMapper.INSTANCE; @@ -31,18 +31,18 @@ public final class SessionSettingsApi { } /** - * Identifies the target session. + * Returns a redacted snapshot of session runtime settings, with secrets and raw feature flags excluded. Internal: the runtime settings shape is a runtime-internal surface and is deliberately kept out of the public SDK, because consumers should not depend on the runtime's internal settings layout. It remains callable in-process and is expected to be reworked as the runtime internals are consolidated. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture snapshot() { - return caller.invoke("session.settings.snapshot", java.util.Map.of("sessionId", this.sessionId), SessionSettingsSnapshotResult.class); + CompletableFuture snapshot() { + return caller.invoke("session.settings.snapshot", java.util.Map.of("sessionId", this.sessionId), SessionSettingsSnapshot.class); } /** - * Named Rust-owned settings predicate to evaluate for this session. + * Evaluates a named Rust-owned settings predicate without exposing raw feature flags. Internal: the raw feature-flag names and composition are runtime-internal, so this predicate-evaluation helper is kept out of the public SDK surface and is callable in-process only. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -51,7 +51,7 @@ public CompletableFuture snapshot() { * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture evaluatePredicate(SessionSettingsEvaluatePredicateParams params) { + CompletableFuture evaluatePredicate(SessionSettingsEvaluatePredicateParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); return caller.invoke("session.settings.evaluatePredicate", _p, SessionSettingsEvaluatePredicateResult.class); diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsBuiltInToolAvailabilitySnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsBuiltInToolAvailabilitySnapshot.java index 8a714d5ccb..a2bf7aac12 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsBuiltInToolAvailabilitySnapshot.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsBuiltInToolAvailabilitySnapshot.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSettingsBuiltInToolAvailabilitySnapshot( +record SessionSettingsBuiltInToolAvailabilitySnapshot( /** Whether the report-progress tool is available. */ @JsonProperty("reportProgress") Boolean reportProgress, /** Whether the create-pull-request tool is available. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateParams.java index a7c1663e28..850f657497 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSettingsEvaluatePredicateParams( +record SessionSettingsEvaluatePredicateParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Predicate name. The runtime owns the raw feature-flag names and composition logic. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateResult.java index 3ab56b8ca6..4fd5a005fd 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsEvaluatePredicateResult.java @@ -10,20 +10,17 @@ 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; /** * Result of evaluating a Rust-owned settings predicate. * - * @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 SessionSettingsEvaluatePredicateResult( +record SessionSettingsEvaluatePredicateResult( /** Whether the named settings predicate evaluated to enabled. */ @JsonProperty("enabled") Boolean enabled ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsJobSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsJobSnapshot.java index 8a807e391c..57a6e9115d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsJobSnapshot.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsJobSnapshot.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSettingsJobSnapshot( +record SessionSettingsJobSnapshot( /** GitHub Actions event type for the job. */ @JsonProperty("eventType") String eventType, /** Whether this is the workflow's trigger job. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsModelSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsModelSnapshot.java index e1eb54a251..04ea8c5b9b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsModelSnapshot.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsModelSnapshot.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSettingsModelSnapshot( +record SessionSettingsModelSnapshot( /** Selected model identifier. */ @JsonProperty("model") String model, /** Default reasoning effort for the selected model. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsOnlineEvaluationSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsOnlineEvaluationSnapshot.java index 999e1a2cbf..458f15f200 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsOnlineEvaluationSnapshot.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsOnlineEvaluationSnapshot.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSettingsOnlineEvaluationSnapshot( +record SessionSettingsOnlineEvaluationSnapshot( /** Whether online evaluation is disabled. */ @JsonProperty("disableOnlineEvaluation") Boolean disableOnlineEvaluation, /** Whether online-evaluation output-file generation is enabled. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsPredicateName.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsPredicateName.java index 6c99c214a8..45d49f3e9c 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsPredicateName.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsPredicateName.java @@ -15,7 +15,7 @@ * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum SessionSettingsPredicateName { +enum SessionSettingsPredicateName { /** The {@code securityToolsEnabled} variant. */ SECURITYTOOLSENABLED("securityToolsEnabled"), /** The {@code thirdPartySecurityPromptEnabled} variant. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsRepoSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsRepoSnapshot.java index 3651346bd8..3ff0fe4684 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsRepoSnapshot.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsRepoSnapshot.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSettingsRepoSnapshot( +record SessionSettingsRepoSnapshot( /** Repository name. */ @JsonProperty("name") String name, /** GitHub repository database ID. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshot.java similarity index 91% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshot.java index 1bcfe166fc..4a347290d3 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshot.java @@ -10,20 +10,17 @@ 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; /** * Redacted, serializable view of session runtime settings for SDK boundary consumers. Secrets and raw feature flags are intentionally excluded. * - * @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 SessionSettingsSnapshotResult( +record SessionSettingsSnapshot( /** Agent runtime version selector copied from the session settings, such as `latest` or a runtime release identifier. */ @JsonProperty("version") String version, /** Name of the SDK client that created the session. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotParams.java index 8bad931529..23585e8d91 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsSnapshotParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSettingsSnapshotParams( +record SessionSettingsSnapshotParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsValidationSnapshot.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsValidationSnapshot.java index 314cc5b2b4..cea60b6119 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsValidationSnapshot.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSettingsValidationSnapshot.java @@ -20,7 +20,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSettingsValidationSnapshot( +record SessionSettingsValidationSnapshot( /** General validation timeout budget in seconds. */ @JsonProperty("timeout") Double timeout, /** Dependabot validation timeout budget in seconds. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellApi.java index b96ac87ea7..6d3187ef75 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellApi.java @@ -31,7 +31,7 @@ public final class SessionShellApi { } /** - * Shell command to run, with optional working directory and timeout in milliseconds. + * Starts a shell command, returning an RPC error if it cannot be spawned. The command runs as the leader of its own process group (POSIX) or in a dedicated job object (Windows), so a forced termination — via "shell.kill", the request timeout, or session disposal — signals that whole group/job rather than only the direct child. Two gaps are worth planning for: a command that exits on its own does not trigger that teardown, and on POSIX a descendant that moves itself into a new session or process group (for example via "setsid") leaves the signalled group, so either can leave a background process running. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture exec(SessionShellExecParams par } /** - * Identifier of a process previously returned by "shell.exec" and the signal to send. + * Sends a signal to a shell process previously started via "shell.exec". The signal targets the command's whole process group (POSIX) or job object (Windows), so descendants still in that group are signalled too, not just the direct child. On POSIX a descendant that moved itself into a new session or process group (for example via "setsid") is no longer in the signalled group and survives. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -63,7 +63,7 @@ public CompletableFuture kill(SessionShellKillParams par } /** - * User-requested shell command and cancellation handle. + * Executes a user-requested shell command through the session runtime. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -79,7 +79,7 @@ public CompletableFuture executeUserRequ } /** - * User-requested shell execution cancellation handle. + * Cancels a user-requested shell command by request ID. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecParams.java index 5ef68032d2..61e8406a6e 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecParams.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Shell command to run, with optional working directory and timeout in milliseconds. + * Shell command to run, with optional working directory and timeout in milliseconds. Spawn failures return an RPC error. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecResult.java index 145d5cbb88..82755320db 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionShellExecResult.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * Identifier of the spawned process, used to correlate streamed output and exit notifications. + * Identifier of the spawned shell process, usable with shell.kill while the process is running. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -24,7 +24,7 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record SessionShellExecResult( - /** Unique identifier for tracking streamed output */ + /** Identifier usable with shell.kill while the process is running */ @JsonProperty("processId") String processId ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsApi.java index f6cceb98b9..e21c294b31 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionSkillsApi.java @@ -31,7 +31,7 @@ public final class SessionSkillsApi { } /** - * Identifies the target session. + * Lists skills available to the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture list() { } /** - * Identifies the target session. + * Returns the skills that have been invoked during this session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -53,7 +53,7 @@ public CompletableFuture getInvoked() { } /** - * Name of the skill to enable for the session. + * Enables a skill for the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -69,7 +69,7 @@ public CompletableFuture enable(SessionSkillsEnableParams params) { } /** - * Name of the skill to disable for the session. + * Disables a skill for the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -85,7 +85,7 @@ public CompletableFuture disable(SessionSkillsDisableParams params) { } /** - * Identifies the target session. + * Reloads skill definitions for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -96,7 +96,7 @@ public CompletableFuture reload() { } /** - * Identifies the target session. + * Ensures the session's skill definitions have been loaded from disk. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksApi.java index c6ca1335de..9e78691586 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTasksApi.java @@ -31,7 +31,7 @@ public final class SessionTasksApi { } /** - * Agent type, prompt, name, and optional description and model override for the new task. + * Starts a background agent task in the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture startAgent(SessionTasksSt } /** - * Identifies the target session. + * Lists background tasks tracked by the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -58,7 +58,7 @@ public CompletableFuture list() { } /** - * Registers or reclaims a client-owned task. + * Registers a client-owned task, or reclaims an orphaned task belonging to the same extension principal. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -74,7 +74,7 @@ public CompletableFuture register(SessionTasksRegist } /** - * Updates a client-owned task. + * Publishes generic progress or a terminal outcome for a client-owned task. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -90,7 +90,7 @@ public CompletableFuture update(SessionTasksUpdatePara } /** - * Identifies the target session. + * Refreshes metadata for any detached background shells the runtime knows about. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -101,7 +101,7 @@ public CompletableFuture refresh() { } /** - * Identifies the target session. + * Waits for all in-flight background tasks and any follow-up turns to settle. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -112,7 +112,7 @@ public CompletableFuture waitForPending() { } /** - * Identifier of the background task to fetch progress for. + * Returns progress information for a background task by ID. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -128,7 +128,7 @@ public CompletableFuture getProgress(SessionTasks } /** - * Identifies the target session. + * Returns the first sync-waiting task that can currently be promoted to background mode. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -139,7 +139,7 @@ public CompletableFuture getCurrentPromo } /** - * Identifier of the task to promote to background mode. + * Promotes an eligible synchronously-waited task so it continues running in the background. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -155,7 +155,7 @@ public CompletableFuture promoteToBackgro } /** - * Identifies the target session. + * Atomically promotes the first promotable sync-waiting task to background mode and returns it. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -166,7 +166,7 @@ public CompletableFuture promoteCu } /** - * Identifier of the background task to cancel. + * Cancels a background task. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -182,7 +182,7 @@ public CompletableFuture cancel(SessionTasksCancelPara } /** - * Identifier of the completed or cancelled task to remove from tracking. + * Removes a completed or cancelled background task from tracking. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -198,7 +198,7 @@ public CompletableFuture remove(SessionTasksRemovePara } /** - * Identifier of the target agent task, message content, and optional sender agent ID. + * Sends a message to a background agent task. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryApi.java index 42589661ef..d74f8366da 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionTelemetryApi.java @@ -31,7 +31,7 @@ public final class SessionTelemetryApi { } /** - * Identifies the target session. + * Gets the telemetry engagement ID currently associated with the session, when available. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture getEngagementId( } /** - * Feature override key/value pairs to attach to subsequent telemetry events from this session. + * Sets feature override key/value pairs to attach to subsequent telemetry events for the session. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsApi.java index 62593a3588..250478499b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsApi.java @@ -31,7 +31,7 @@ public final class SessionToolsApi { } /** - * A tool name and arguments to execute through the session's native invocation pipeline. + * Executes one tool from the session's currently offered tool set through the native invocation pipeline. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture execute(SessionToolsExecuteParams params) { } /** - * Options controlling how Rust-owned built-in tool descriptors are materialized. + * Returns the Rust-owned built-in tool descriptors used to construct the session's offered tool set. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -63,7 +63,7 @@ public CompletableFuture getBuiltinDesc } /** - * Task-completion tool arguments and final result used to build a label-safe session event payload. + * Projects a completed task_complete tool call into its label-safe session event payload. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -79,7 +79,7 @@ public CompletableFuture taskCompleteEv } /** - * Pending external tool call request ID, with the tool result or an error describing why it failed. + * Provides the result for a pending external tool call. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -95,7 +95,18 @@ public CompletableFuture handlePendingT } /** - * Identifies the target session. + * Lists the external tool calls of the session and its sub-agents that are still waiting for session.tools.handlePendingToolCall. + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ + @CopilotExperimental + public CompletableFuture listPendingRequests() { + return caller.invoke("session.tools.listPendingRequests", java.util.Map.of("sessionId", this.sessionId), SessionToolsListPendingRequestsResult.class); + } + + /** + * Resolves, builds, and validates the runtime tool list for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -106,7 +117,7 @@ public CompletableFuture initializeAndValidate() { } /** - * Identifies the target session. + * Returns lightweight metadata for the session's currently initialized tools. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -117,7 +128,7 @@ public CompletableFuture getCurrentMetadat } /** - * Complete externally implemented tool list for the calling connection. An empty list removes every tool previously supplied by that connection. + * Atomically replaces the complete externally implemented tool list supplied by the calling connection. Built-in, MCP/plugin, extension-discovered, subagent, and tools supplied by other connections remain unchanged. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -133,7 +144,7 @@ public CompletableFuture set(SessionToolsSetParams params) { } /** - * Subagent settings to apply to the current session + * Sets the current session's live subagent settings override, which takes precedence over persisted user settings until cleared. Persisted user settings remain the source of truth for future sessions. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostMaterializeSessionResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsListPendingRequestsParams.java similarity index 82% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostMaterializeSessionResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsListPendingRequestsParams.java index ea45e0bbbc..8a10167180 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/HostMaterializeSessionResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsListPendingRequestsParams.java @@ -14,7 +14,7 @@ import javax.annotation.processing.Generated; /** - * The resident session the application has materialized on its own connection. + * No parameters; returns the external tool calls of the session and its sub-agents that are still waiting for a result. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -23,8 +23,8 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record HostMaterializeSessionResult( - /** Runtime session UUID materialized on the application's SDK connection. */ +public record SessionToolsListPendingRequestsParams( + /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsListPendingRequestsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsListPendingRequestsResult.java new file mode 100644 index 0000000000..c7b17b4928 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionToolsListPendingRequestsResult.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; + +/** + * External tool calls still waiting for a 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 SessionToolsListPendingRequestsResult( + /** 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. */ + @JsonProperty("items") List items +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiApi.java index b3e16d7c27..5187b51774 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiApi.java @@ -31,7 +31,7 @@ public final class SessionUiApi { } /** - * Transient question to answer without adding it to conversation history. + * Runs a transient no-tools model query against the current conversation context. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture ephemeralQuery(SessionUi } /** - * Prompt message and JSON schema describing the form fields to elicit from the user. + * Requests structured input from a UI-capable client. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -63,7 +63,7 @@ public CompletableFuture elicitation(SessionUiElicit } /** - * Pending elicitation request ID and the user's response (accept/decline/cancel + form values). + * Provides the user response for a pending elicitation request. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -79,7 +79,23 @@ public CompletableFuture handlePendingE } /** - * Request ID of a pending `user_input.requested` event and the user's response. + * Resolves a pending elicitation request after direct interaction in the trusted in-process client. Only an accepted response to the built-in ask_user tool can become trusted human evidence. + *

+ * 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 + CompletableFuture handleHumanAskUser(SessionUiHandleHumanAskUserParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.ui.handleHumanAskUser", _p, UIElicitationResult.class); + } + + /** + * Resolves a pending `user_input.requested` event with the user's response. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -95,7 +111,23 @@ public CompletableFuture handlePendingUse } /** - * Request ID of a pending `sampling.requested` event and an optional sampling result payload (omit to reject). + * Resolves a pending `user_input.requested` event after direct interaction in the trusted in-process client. + *

+ * 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 + CompletableFuture handleHumanUserInput(SessionUiHandleHumanUserInputParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.ui.handleHumanUserInput", _p, UIHandlePendingResult.class); + } + + /** + * Resolves a pending `sampling.requested` event with a sampling result, or rejects it. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -111,7 +143,7 @@ public CompletableFuture handlePendingSamp } /** - * Request ID of a pending `auto_mode_switch.requested` event and the user's response. + * Resolves a pending `auto_mode_switch.requested` event with the user's accept/decline decision. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -127,7 +159,7 @@ public CompletableFuture handlePendi } /** - * Request ID of a pending `session_limits_exhausted.requested` event and the user's selected limit action. + * Resolves a pending `session_limits_exhausted.requested` event with the user's selected limit action. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -143,7 +175,7 @@ public CompletableFuture han } /** - * Request ID of a pending `exit_plan_mode.requested` event and the user's response. + * Resolves a pending `exit_plan_mode.requested` event with the user's response. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -159,7 +191,23 @@ public CompletableFuture handlePending } /** - * Identifies the target session. + * Resolves a pending `exit_plan_mode.requested` event after direct interaction in the trusted in-process client. + *

+ * 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 + CompletableFuture handleHumanExitPlanMode(SessionUiHandleHumanExitPlanModeParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.ui.handleHumanExitPlanMode", _p, UIHandlePendingResult.class); + } + + /** + * Registers an in-process handler for auto-mode-switch requests so the server bridge skips dispatch. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -170,7 +218,7 @@ public CompletableFuture reg } /** - * Opaque handle previously returned by `registerDirectAutoModeSwitchHandler` to release. + * Unregisters a previously-registered in-process auto-mode-switch handler by its opaque handle. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandleHumanAskUserParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandleHumanAskUserParams.java new file mode 100644 index 0000000000..de9e0419e7 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandleHumanAskUserParams.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; + +/** + * Pending elicitation request ID and the user's response (accept/decline/cancel + form values). + * + * @apiNote This method is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SessionUiHandleHumanAskUserParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** The unique request ID from the elicitation.requested event */ + @JsonProperty("requestId") String requestId, + /** The elicitation response (accept with form values, decline, or cancel) */ + @JsonProperty("result") UIElicitationResponse result +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandleHumanExitPlanModeParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandleHumanExitPlanModeParams.java new file mode 100644 index 0000000000..aab4e6d7cb --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandleHumanExitPlanModeParams.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; + +/** + * Request ID of a pending `exit_plan_mode.requested` event and the user's response. + * + * @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 SessionUiHandleHumanExitPlanModeParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** The unique request ID from the exit_plan_mode.requested event */ + @JsonProperty("requestId") String requestId, + /** User response for a pending exit-plan-mode request, with approval state, selected action, auto-approve flag, and feedback. */ + @JsonProperty("response") UIExitPlanModeResponse response +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandleHumanUserInputParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandleHumanUserInputParams.java new file mode 100644 index 0000000000..45629b962d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUiHandleHumanUserInputParams.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; + +/** + * Request ID of a pending `user_input.requested` event and the user's response. + * + * @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 SessionUiHandleHumanUserInputParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** The unique request ID from the user_input.requested event */ + @JsonProperty("requestId") String requestId, + /** User response for a pending user-input request, with answer text and whether it was typed freeform. */ + @JsonProperty("response") UIUserInputResponse response +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageApi.java index 16ded9d6b6..75a19eadb1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageApi.java @@ -19,6 +19,8 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") public final class SessionUsageApi { + private static final com.fasterxml.jackson.databind.ObjectMapper MAPPER = RpcMapper.INSTANCE; + private final RpcCaller caller; private final String sessionId; @@ -29,7 +31,7 @@ public final class SessionUsageApi { } /** - * Identifies the target session. + * Gets accumulated usage metrics for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -39,4 +41,20 @@ public CompletableFuture getMetrics() { return caller.invoke("session.usage.getMetrics", java.util.Map.of("sessionId", this.sessionId), SessionUsageGetMetricsResult.class); } + /** + * Internal host-only replacement of absolute code-change totals reported by a relay host. + *

+ * 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 + CompletableFuture setCodeChanges(SessionUsageSetCodeChangesParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.usage.setCodeChanges", _p, Void.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsResult.java index a38afd7042..1a771a239a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageGetMetricsResult.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.github.copilot.CopilotExperimental; import java.time.OffsetDateTime; +import java.util.List; import java.util.Map; import javax.annotation.processing.Generated; @@ -26,6 +27,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record SessionUsageGetMetricsResult( + /** Completeness of totalNanoAiu. Numeric totals remain the reported subtotal; zero is not a claim of free usage when unavailable. */ + @JsonProperty("aiCreditsStatus") AiCreditsStatus aiCreditsStatus, /** Total user-initiated premium request cost across all models (may be fractional due to multipliers) */ @JsonProperty("totalPremiumRequestCost") Double totalPremiumRequestCost, /** Raw count of user-initiated API requests */ @@ -42,6 +45,9 @@ public record SessionUsageGetMetricsResult( @JsonProperty("codeChanges") UsageMetricsCodeChanges codeChanges, /** Per-model token and request metrics, keyed by model identifier */ @JsonProperty("modelMetrics") Map modelMetrics, + /** Provider-attributed model metrics, ordered by first observed provider and model. +Null attribution preserves historical usage whose provider or model was not recorded. */ + @JsonProperty("providerModelMetrics") List providerModelMetrics, /** Per-agent usage metrics, keyed by agent instance identifier. The main conversation uses the stable key `main`. */ @JsonProperty("agentMetrics") Map agentMetrics, /** Currently active model identifier */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageSetCodeChangesParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageSetCodeChangesParams.java new file mode 100644 index 0000000000..d8a963a75a --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionUsageSetCodeChangesParams.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; + +/** + * Internal absolute code-change totals reported by the owning host. + * + * @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 SessionUsageSetCodeChangesParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Absolute added-line total, replacing the previous reading. */ + @JsonProperty("linesAdded") Double linesAdded, + /** Absolute removed-line total, replacing the previous reading. */ + @JsonProperty("linesRemoved") Double linesRemoved, + /** Absolute changed-file count; omission preserves the previous count. */ + @JsonProperty("filesCount") Double filesCount +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java index 54f38c2614..b942828e10 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionVisibilityApi.java @@ -31,7 +31,7 @@ public final class SessionVisibilityApi { } /** - * Identifies the target session. + * Returns the session's current Mission Control sharing status and shareable GitHub URL. Reflects whether the synced session is visible to repository readers ("repo") or restricted to its creator and collaborators ("unshared"). * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +42,7 @@ public CompletableFuture get() { } /** - * Desired sharing status for the session. + * Sets the session's Mission Control sharing status, controlling whether the synced session is visible to repository readers. Returns the effective status and shareable GitHub URL after the change. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowApi.java index adffbd7302..e746033b39 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowApi.java @@ -35,7 +35,7 @@ public final class SessionWorkflowApi { } /** - * Parameters for invoking a registered workflow. + * Runs a registered dynamic workflow by name at the top level. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -51,7 +51,7 @@ public CompletableFuture run(SessionWorkflowRunParams } /** - * Parameters for resuming a workflow run from its persisted identity. + * Resumes a dynamic workflow run using its persisted name, arguments, journal, and accounting. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -67,7 +67,7 @@ public CompletableFuture resume(SessionWorkflowResu } /** - * Internal parameters for invoking a registered workflow from a tool. + * Internal tool-originated dynamic workflow invocation. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -76,14 +76,14 @@ public CompletableFuture resume(SessionWorkflowResu * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture runFromTool(SessionWorkflowRunFromToolParams params) { + CompletableFuture runFromTool(SessionWorkflowRunFromToolParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.workflow.runFromTool", _p, SessionWorkflowRunFromToolResult.class); + return caller.invoke("session.workflow.runFromTool", _p, WorkflowRunResult.class); } /** - * Internal parameters for resuming a workflow run from a tool. + * Internal tool-originated dynamic workflow resume. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -92,14 +92,14 @@ public CompletableFuture runFromTool(SessionWo * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture resumeFromTool(SessionWorkflowResumeFromToolParams params) { + CompletableFuture resumeFromTool(SessionWorkflowResumeFromToolParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.workflow.resumeFromTool", _p, SessionWorkflowResumeFromToolResult.class); + return caller.invoke("session.workflow.resumeFromTool", _p, WorkflowResumeResult.class); } /** - * Parameters for retrieving a workflow run. + * Gets the current or settled envelope for a dynamic workflow run. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -115,7 +115,7 @@ public CompletableFuture getRun(SessionWorkflowGetR } /** - * Parameters for paging workflow runs. + * Lists durable dynamic workflow runs for this session in creation order. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -131,7 +131,7 @@ public CompletableFuture listRuns(SessionWorkflow } /** - * Parameters for retrieving a workflow run. + * Gets durable and live observability detail for one dynamic workflow run. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -147,7 +147,7 @@ public CompletableFuture getRunDetail(Session } /** - * Parameters for paging workflow progress. + * Pages durable progress for one dynamic workflow run. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -163,7 +163,7 @@ public CompletableFuture getRunProgress(Ses } /** - * Parameters for cancelling a workflow run. + * Requests cancellation of a dynamic workflow run and returns its run envelope. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -179,7 +179,7 @@ public CompletableFuture cancel(SessionWorkflowCanc } /** - * Parameters for pausing a running workflow. + * Pauses a running dynamic workflow and returns its settled run envelope. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -195,7 +195,7 @@ public CompletableFuture pause(SessionWorkflowPauseP } /** - * Parameters for an owned durable pause checkpoint. + * Atomically pauses an owned dynamic workflow attempt at a durable checkpoint. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -204,14 +204,14 @@ public CompletableFuture pause(SessionWorkflowPauseP * @since 1.0.0 */ @CopilotExperimental - public CompletableFuture pauseAtCheckpoint(SessionWorkflowPauseAtCheckpointParams params) { + CompletableFuture pauseAtCheckpoint(SessionWorkflowPauseAtCheckpointParams params) { com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); _p.put("sessionId", this.sessionId); - return caller.invoke("session.workflow.pauseAtCheckpoint", _p, SessionWorkflowPauseAtCheckpointResult.class); + return caller.invoke("session.workflow.pauseAtCheckpoint", _p, WorkflowPauseCheckpointResult.class); } /** - * Parameters for recording workflow progress. + * Records a batch of ordered dynamic workflow progress lines. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -227,7 +227,7 @@ public CompletableFuture log(SessionWorkflowLogParams params) { } /** - * Parameters for one workflow-scoped subagent call. + * Runs one dynamic-workflow-scoped subagent and returns its result. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowJournalApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowJournalApi.java index 0909931439..70bc1b47e2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowJournalApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowJournalApi.java @@ -31,7 +31,7 @@ public final class SessionWorkflowJournalApi { } /** - * Parameters for reading a workflow journal entry. + * Reads a memoized dynamic workflow journal entry. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -47,7 +47,7 @@ public CompletableFuture get(SessionWorkflowJou } /** - * Parameters for storing a workflow journal entry. + * Stores a memoized dynamic workflow journal entry. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowPauseAtCheckpointParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowPauseAtCheckpointParams.java index 5000a5c4d8..5ca48e5ee6 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowPauseAtCheckpointParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowPauseAtCheckpointParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionWorkflowPauseAtCheckpointParams( +record SessionWorkflowPauseAtCheckpointParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Workflow run identifier. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowResumeFromToolParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowResumeFromToolParams.java index c9630f6ede..84e0eca0fb 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowResumeFromToolParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowResumeFromToolParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionWorkflowResumeFromToolParams( +record SessionWorkflowResumeFromToolParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Workflow run identifier. */ 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 8fc9661ea4..d68976fb76 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 @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionWorkflowRunFromToolParams( +record SessionWorkflowRunFromToolParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Registered workflow name. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunFromToolResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunFromToolResult.java deleted file mode 100644 index d5ab4d5e6e..0000000000 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowRunFromToolResult.java +++ /dev/null @@ -1,46 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All 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; - -/** - * Complete current or terminal workflow run envelope. - * - * @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 SessionWorkflowRunFromToolResult( - /** Workflow run identifier. */ - @JsonProperty("runId") String runId, - /** One-based execution attempt represented by this envelope. Absent before the first attempt starts or when returned by an older runtime. */ - @JsonProperty("attempt") Long attempt, - /** Current or terminal workflow run status. */ - @JsonProperty("status") WorkflowRunStatus status, - /** Completed workflow result. */ - @JsonProperty("result") Object result, - /** Error message for an errored run. */ - @JsonProperty("error") String error, - /** Machine-readable failure details for a halted or errored run. */ - @JsonProperty("failure") Object failure, - /** Reason for a halted or cancelled run. */ - @JsonProperty("reason") String reason, - /** Partial journal and progress snapshot for a halted, cancelled, or errored run. */ - @JsonProperty("snapshot") Object snapshot, - /** Structured pause initiator metadata for a paused attempt. */ - @JsonProperty("pauseInfo") Object pauseInfo -) { -} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContextWithClient.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContextWithClient.java new file mode 100644 index 0000000000..d909895e2d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkingDirectoryContextWithClient.java @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * A working-directory context together with the client that produced it. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SessionWorkingDirectoryContextWithClient( + /** Current working directory path */ + @JsonProperty("cwd") String cwd, + /** Root directory of the git repository */ + @JsonProperty("gitRoot") String gitRoot, + /** Repository identifier derived from the git remote URL */ + @JsonProperty("repository") String repository, + /** Hosting platform type of the repository */ + @JsonProperty("hostType") String hostType, + /** Current git branch name */ + @JsonProperty("branch") String branch, + /** Raw host string from the git remote URL */ + @JsonProperty("repositoryHost") String repositoryHost, + /** Head commit of the current git branch */ + @JsonProperty("headCommit") String headCommit, + /** Merge-base commit SHA */ + @JsonProperty("baseCommit") String baseCommit, + /** Name of the client that created the session */ + @JsonProperty("clientName") String clientName +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesApi.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesApi.java index 71a3d1ea1b..c65bd3854d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesApi.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesApi.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; @@ -31,7 +32,7 @@ public final class SessionWorkspacesApi { } /** - * Identifies the target session. + * Gets current workspace metadata for the session. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -42,7 +43,7 @@ public CompletableFuture getWorkspace() { } /** - * Workspace metadata fields to update. + * Updates workspace metadata for a local session and returns the refreshed workspace. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -58,7 +59,7 @@ public CompletableFuture updateMetadata(S } /** - * Optional session context used when creating a local workspace. + * Ensures a local session workspace exists and returns it. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -74,7 +75,7 @@ public CompletableFuture ensure(SessionWorkspaces } /** - * Identifies the target session. + * Lists files stored in the session workspace files directory. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -85,7 +86,7 @@ public CompletableFuture listFiles() { } /** - * Relative path of the workspace file to read. + * Reads a file from the session workspace files directory. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -101,7 +102,7 @@ public CompletableFuture readFile(SessionWorksp } /** - * Relative path and UTF-8 content for the workspace file to create or overwrite. + * Creates or overwrites a file in the session workspace files directory. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -117,7 +118,7 @@ public CompletableFuture createFile(SessionWorkspacesCreateFileParams para } /** - * Relative path of the workspace file or directory to inspect. + * Returns metadata for a file or directory in the session workspace files directory. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -133,7 +134,7 @@ public CompletableFuture statFile(SessionWorksp } /** - * Directory to create within the session workspace files directory. + * Creates a directory in the session workspace files directory. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -149,7 +150,7 @@ public CompletableFuture createDirectory(SessionWorkspacesCreateDirectoryP } /** - * File or directory to remove from the session workspace files directory. + * Removes a file or directory from the session workspace files directory. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -165,7 +166,7 @@ public CompletableFuture removePath(SessionWorkspacesRemovePathParams para } /** - * Source and destination paths for a rename within the session workspace files directory. + * Renames a file or directory within the session workspace files directory. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -181,7 +182,7 @@ public CompletableFuture renamePath(SessionWorkspacesRenamePathParams para } /** - * Identifies the target session. + * Lists workspace checkpoints in chronological order. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -192,7 +193,7 @@ public CompletableFuture listCheckpoints } /** - * Checkpoint number to read. + * Reads the content of a workspace checkpoint by number. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -208,7 +209,7 @@ public CompletableFuture readCheckpoint(S } /** - * Compaction summary checkpoint to persist. + * Adds a compaction summary checkpoint to the local session workspace. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -224,7 +225,7 @@ public CompletableFuture addSummary(SessionWo } /** - * Rollback point for local workspace summaries. + * Truncates local workspace compaction summaries after a rollback. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -240,7 +241,7 @@ public CompletableFuture truncateSumma } /** - * Identifies the target session. + * Reads the autopilot objective state file from the local session workspace. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -251,7 +252,7 @@ public CompletableFuture readAuto } /** - * Autopilot objective file content to persist. + * Writes the autopilot objective state file in the local session workspace. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -267,7 +268,7 @@ public CompletableFuture writeAu } /** - * Identifies the target session. + * Deletes the autopilot objective state file from the local session workspace. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -278,7 +279,7 @@ public CompletableFuture delete } /** - * Identifies the target session. + * Checks whether the local session workspace has an autopilot objective state file. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 @@ -289,7 +290,7 @@ public CompletableFuture autopi } /** - * Pasted content to save as a UTF-8 file in the session workspace. + * Saves pasted content as a UTF-8 file in the session workspace. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -305,7 +306,7 @@ public CompletableFuture saveLargePaste(S } /** - * Parameters for computing a workspace diff. + * Computes a diff for the session workspace. Never rejects for a busy session: a `session`-mode diff that cannot read the session's file-change captures falls back to an unstaged git diff with `isFallback: true` and reports why in `unavailableReason`. *

* Note: the {@code sessionId} field in the params record is overridden * by the session-scoped wrapper; any value provided is ignored. @@ -320,4 +321,19 @@ public CompletableFuture diff(SessionWorkspacesDiff return caller.invoke("session.workspaces.diff", _p, SessionWorkspacesDiffResult.class); } + /** + * Computes a diff for the session workspace. Never rejects for a busy session: a `session`-mode diff that cannot read the session's file-change captures falls back to an unstaged git diff with `isFallback: true` and reports why in `unavailableReason`. + *

+ * 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 diff(SessionWorkspacesDiffRequest request) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(Objects.requireNonNull(request, "request")); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.workspaces.diff", _p, SessionWorkspacesDiffResult.class); + } + } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffRequest.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffRequest.java new file mode 100644 index 0000000000..cf12c01e63 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkspacesDiffRequest.java @@ -0,0 +1,98 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All 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.Objects; +import javax.annotation.processing.Generated; + +/** + * Parameters for computing a workspace diff. + *

+ * 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 SessionWorkspacesDiffRequest { + + /** Diff mode requested by the client. */ + @JsonProperty("mode") + private final WorkspaceDiffMode mode; + + /** When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. */ + @JsonProperty("ignoreWhitespace") + private Boolean ignoreWhitespace; + + /** 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. */ + @JsonProperty("includeContents") + private Boolean includeContents; + + /** + * Creates a request with its required inputs. + * + * @param mode Diff mode requested by the client. + */ + public SessionWorkspacesDiffRequest(WorkspaceDiffMode mode) { + this.mode = Objects.requireNonNull(mode, "mode"); + } + + /** + * Returns the {@code mode} property. + * + * @return Diff mode requested by the client. + */ + public WorkspaceDiffMode getMode() { + return mode; + } + + /** + * Returns the {@code ignoreWhitespace} property. + * + * @return When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. + */ + public Boolean getIgnoreWhitespace() { + return ignoreWhitespace; + } + + /** + * Returns the {@code includeContents} property. + * + * @return 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. + */ + public Boolean getIncludeContents() { + return includeContents; + } + + /** + * Sets the {@code ignoreWhitespace} property. + * + * @param value When true, ignore whitespace-only changes (git `--ignore-all-space`). Defaults to false. + * @return this request + */ + public SessionWorkspacesDiffRequest setIgnoreWhitespace(Boolean value) { + this.ignoreWhitespace = value; + return this; + } + + /** + * Sets the {@code includeContents} property. + * + * @param value 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. + * @return this request + */ + public SessionWorkspacesDiffRequest setIncludeContents(Boolean value) { + this.includeContents = value; + return this; + } +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConfigureSessionExtensionsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConfigureSessionExtensionsParams.java index 83d2d9c61c..e39e43456a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConfigureSessionExtensionsParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsConfigureSessionExtensionsParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionsConfigureSessionExtensionsParams( +record SessionsConfigureSessionExtensionsParams( /** Session to attach the extension controller delegate to. */ @JsonProperty("sessionId") String sessionId, /** In-process ExtensionController delegate (CLI-only optimization). Marked internal: this field is excluded from the public SDK surface. The post-SDK extension surface exposes list/enable/disable/reload via dedicated RPCs served by the runtime. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCreateWorkspaceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCreateWorkspaceParams.java new file mode 100644 index 0000000000..04c54ddedb --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCreateWorkspaceParams.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; + +/** + * Identity, state location and starting context for a workspace record. + * + * @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 SessionsCreateWorkspaceParams( + /** Session ID the workspace record belongs to */ + @JsonProperty("sessionId") String sessionId, + /** 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. */ + @JsonProperty("sessionStatePath") String sessionStatePath, + /** `windows` (any letter case) selects Windows path rules. Any other value selects POSIX path rules. */ + @JsonProperty("convention") String convention, + /** 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`. */ + @JsonProperty("context") SessionWorkingDirectoryContextWithClient context, + /** User-supplied display name for the workspace */ + @JsonProperty("name") String name +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCreateWorkspaceResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCreateWorkspaceResult.java new file mode 100644 index 0000000000..b2cbe17721 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsCreateWorkspaceResult.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 workspace record that was written. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SessionsCreateWorkspaceResult( + /** The created workspace record, as JSON */ + @JsonProperty("workspaceJson") String workspaceJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsDeleteParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsDeleteParams.java index 788811e348..ee7d6e508d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsDeleteParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsDeleteParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionsDeleteParams( +record SessionsDeleteParams( /** Session ID to delete */ @JsonProperty("sessionId") String sessionId, /** Internal resolved session directory path to delete */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountParams.java index 28ea6d8c24..0710387f91 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionsGetBoardEntryCountParams( +record SessionsGetBoardEntryCountParams( /** Session ID whose board entry count should be returned. */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountResult.java index b0c70169ed..fe0412c114 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetBoardEntryCountResult.java @@ -10,20 +10,17 @@ 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; /** * Dynamic-context board entry count, when available. * - * @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 SessionsGetBoardEntryCountResult( +record SessionsGetBoardEntryCountResult( /** Board entry count, when available. */ @JsonProperty("count") Long count ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathParams.java index 0b8c72e581..2953a1cdbd 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionsGetEventFilePathParams( +record SessionsGetEventFilePathParams( /** Session ID whose event-log file path to compute */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathResult.java index c403264a9d..cea6c10f29 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetEventFilePathResult.java @@ -10,20 +10,17 @@ 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; /** * Absolute path to the session's events.jsonl file on disk. * - * @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 SessionsGetEventFilePathResult( +record SessionsGetEventFilePathResult( /** Absolute path to the session's events.jsonl file */ @JsonProperty("filePath") String filePath ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataParams.java index cfa2e6326e..0e8dfe14a1 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionsGetMetadataParams( +record SessionsGetMetadataParams( /** Session ID to inspect */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataResult.java index 6546b00fe0..54571785fe 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetMetadataResult.java @@ -10,20 +10,17 @@ 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; /** * Persisted local session metadata when the session exists. * - * @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 SessionsGetMetadataResult( +record SessionsGetMetadataResult( /** Local session metadata, omitted when the session does not exist. */ @JsonProperty("session") LocalSessionMetadataValue session ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableParams.java index 976efd6751..98622cf322 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionsGetPersistedRemoteSteerableParams( +record SessionsGetPersistedRemoteSteerableParams( /** Session ID to look up the persisted remote-steerable flag for */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableResult.java index 97127d8560..4ee41920f3 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsGetPersistedRemoteSteerableResult.java @@ -10,20 +10,17 @@ 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 session's persisted remote-steerable flag, or omitted when no value has been persisted. * - * @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 SessionsGetPersistedRemoteSteerableResult( +record SessionsGetPersistedRemoteSteerableResult( /** The session's persisted remote-steerable flag if recorded; omitted when no value has been persisted */ @JsonProperty("remoteSteerable") Boolean remoteSteerable ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsParams.java index 4e453121bc..a8de88c58b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionsListNonEmptySessionIdsParams( +record SessionsListNonEmptySessionIdsParams( /** Maximum number of session IDs to return. */ @JsonProperty("limit") Long limit ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsResult.java index 51cc266e63..1a20c17a8d 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsListNonEmptySessionIdsResult.java @@ -10,21 +10,18 @@ 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; /** * Recent local session IDs that contain user-visible history. * - * @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 SessionsListNonEmptySessionIdsResult( +record SessionsListNonEmptySessionIdsResult( /** Session IDs ordered newest-first. */ @JsonProperty("sessionIds") List sessionIds ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadWorkspaceParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadWorkspaceParams.java new file mode 100644 index 0000000000..bb4b83da26 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadWorkspaceParams.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; + +/** + * Where the session's state lives, as a root directory and the session ID under 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 SessionsLoadWorkspaceParams( + /** Root directory every session's state directory sits under */ + @JsonProperty("sessionsHome") String sessionsHome, + /** 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. */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadWorkspaceResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadWorkspaceResult.java new file mode 100644 index 0000000000..f17c2c85d6 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsLoadWorkspaceResult.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 workspace record on disk, omitted when the session has none. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +record SessionsLoadWorkspaceResult( + /** The workspace record, as JSON. Omitted when the record does not exist. */ + @JsonProperty("workspaceJson") String workspaceJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsUpdateWorkspaceFieldsParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsUpdateWorkspaceFieldsParams.java new file mode 100644 index 0000000000..770e8269be --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionsUpdateWorkspaceFieldsParams.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; + +/** + * 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. + * + * @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 SessionsUpdateWorkspaceFieldsParams( + /** Root directory every session's state directory sits under */ + @JsonProperty("sessionsHome") String sessionsHome, + /** 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. */ + @JsonProperty("sessionId") String sessionId, + /** 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. */ + @JsonProperty("fieldsJson") String fieldsJson +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderDescriptor.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderDescriptor.java index a678b95fa3..64a287661b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderDescriptor.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderDescriptor.java @@ -13,14 +13,14 @@ import javax.annotation.processing.Generated; /** - * Catalog-only metadata for one SDK-provided skill. The complete SKILL.md is fetched separately and lazily. + * Authoritative catalog metadata for one SDK-provided skill. The skill's SKILL.md text is fetched separately and lazily. * * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SkillProviderDescriptor( +record SkillProviderDescriptor( /** Invocation and display name. */ @JsonProperty("name") String name, /** Description used in skill catalogs without fetching content. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderListParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderListParams.java index e11fcf0fbd..5e351adc89 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderListParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderListParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SkillProviderListParams( +record SkillProviderListParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderListResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderListResult.java index 228ec9240b..aac59a687a 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderListResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderListResult.java @@ -10,21 +10,18 @@ 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; /** * Catalog metadata returned by an SDK session's skill provider. Catalogs are limited to 1024 descriptors and 1 MiB of aggregate metadata. * - * @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 SkillProviderListResult( +record SkillProviderListResult( /** Skill descriptors in provider order. Invocation names must be unique under case-insensitive comparison. */ @JsonProperty("skills") List skills ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderReadParams.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderReadParams.java index 895078384b..8da60c6e0f 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderReadParams.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderReadParams.java @@ -23,7 +23,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SkillProviderReadParams( +record SkillProviderReadParams( /** Target session identifier */ @JsonProperty("sessionId") String sessionId, /** Invocation name of the skill to read. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderReadResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderReadResult.java index 0f17696565..056b65f811 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderReadResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SkillProviderReadResult.java @@ -10,21 +10,18 @@ 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; /** - * Complete text-only SKILL.md content returned by an SDK session's skill provider. Related files and assets are not supported. + * Text-only SKILL.md content returned by an SDK session's skill provider. YAML frontmatter is optional: fields it omits come from the catalog descriptor, fields it declares must match the descriptor, and `allowed-tools` is read only from frontmatter. Related files and assets are not supported. * - * @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 SkillProviderReadResult( - /** Complete SKILL.md text. The runtime enforces a 1 MiB UTF-8 byte limit. */ +record SkillProviderReadResult( + /** SKILL.md text, with or without YAML frontmatter, or null when the provider has no skill with the requested name. The runtime enforces a 1 MiB UTF-8 byte limit. */ @JsonProperty("markdown") String markdown ) { } diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/TaskKind.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/TaskKind.java index 413859db20..5db08809c2 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/TaskKind.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/TaskKind.java @@ -15,7 +15,7 @@ * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum TaskKind { +enum TaskKind { /** The {@code agent} variant. */ AGENT("agent"), /** The {@code shell} variant. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResult.java new file mode 100644 index 0000000000..7b5dbb2d00 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIElicitationResult.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; + +/** + * Indicates whether the elicitation response was accepted; false if it was already resolved by another client. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UIElicitationResult( + /** Whether the response was accepted. False if the request was already resolved by another client. */ + @JsonProperty("success") Boolean success +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIHandlePendingResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIHandlePendingResult.java new file mode 100644 index 0000000000..a71e3af6b3 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UIHandlePendingResult.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; + +/** + * Indicates whether the pending UI request was resolved by this call. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record 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. */ + @JsonProperty("success") Boolean success +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetric.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetric.java index ed5f093054..879402975b 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetric.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsModelMetric.java @@ -23,6 +23,8 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public record UsageMetricsModelMetric( + /** Completeness of this model's reported AI-credit subtotal. Explicitly reported zero is complete, not unavailable. */ + @JsonProperty("aiCreditsStatus") AiCreditsStatus aiCreditsStatus, /** Request count and cost metrics for this model */ @JsonProperty("requests") UsageMetricsModelMetricRequests requests, /** Token usage metrics for this model */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsProviderModelMetric.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsProviderModelMetric.java new file mode 100644 index 0000000000..b4955e8c8d --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UsageMetricsProviderModelMetric.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; + +/** + * Usage for one recorded provider and model, without merging identical model IDs across providers. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record UsageMetricsProviderModelMetric( + /** Provider identity and product label captured when the call was dispatched; null when unknown. */ + @JsonProperty("provider") ModelProviderRef provider, + /** Model identity, or null for legacy aggregate-only usage. */ + @JsonProperty("modelId") String modelId, + /** Model display name captured at call time, when known. */ + @JsonProperty("modelDisplayName") String modelDisplayName, + /** Request, token, and cost totals for this provider/model. */ + @JsonProperty("metrics") UsageMetricsModelMetric metrics +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java index c94e90fcc6..ac7b3cf933 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/UserSettingsGetResult.java @@ -15,7 +15,7 @@ import javax.annotation.processing.Generated; /** - * Per-key metadata for every known user setting (settings.json overlaid with the legacy config.json, config.json wins), including settings left at their default. Excludes repository- and enterprise-managed overrides. + * Per-key metadata for every known user setting in settings.json, including settings left at their default. Excludes repository- and enterprise-managed overrides. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowPauseCheckpointAction.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowPauseCheckpointAction.java index 9c034a66a9..e6a7140479 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowPauseCheckpointAction.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowPauseCheckpointAction.java @@ -15,7 +15,7 @@ * @since 1.0.0 */ @javax.annotation.processing.Generated("copilot-sdk-codegen") -public enum WorkflowPauseCheckpointAction { +enum WorkflowPauseCheckpointAction { /** The {@code continue} variant. */ CONTINUE("continue"), /** The {@code pause} variant. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowPauseAtCheckpointResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowPauseCheckpointResult.java similarity index 74% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowPauseAtCheckpointResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowPauseCheckpointResult.java index 9c2a46ab38..653ffa67cb 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowPauseAtCheckpointResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowPauseCheckpointResult.java @@ -10,20 +10,12 @@ 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; -/** - * Result for the {@code session.workflow.pauseAtCheckpoint} RPC method. - * - * @apiNote This method is experimental and may change in a future version. - * @since 1.0.0 - */ -@CopilotExperimental @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionWorkflowPauseAtCheckpointResult( +record WorkflowPauseCheckpointResult( /** Whether this execution attempt must pause or may continue. */ @JsonProperty("action") WorkflowPauseCheckpointAction action ) { diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowResumeFromToolResult.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowResumeResult.java similarity index 84% rename from java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowResumeFromToolResult.java rename to java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowResumeResult.java index 65dc8bfbfe..e030bde8b4 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/SessionWorkflowResumeFromToolResult.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowResumeResult.java @@ -10,20 +10,17 @@ 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; /** * Resolved persisted workflow identity and resumed run envelope. * - * @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 SessionWorkflowResumeFromToolResult( +public record WorkflowResumeResult( /** Persisted workflow name resolved for the resumed run. */ @JsonProperty("workflowName") String workflowName, /** Terminal resumed run envelope. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowToolRunOptions.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowToolRunOptions.java index 3a0f5099cf..6211f5a376 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowToolRunOptions.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkflowToolRunOptions.java @@ -24,7 +24,7 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record WorkflowToolRunOptions( +record WorkflowToolRunOptions( /** Per-invocation resource ceiling overrides. */ @JsonProperty("limits") WorkflowRunLimits limits, /** Run identifier whose journal and progress should seed this resumed run. */ diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffContents.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffContents.java new file mode 100644 index 0000000000..7ffdaa1862 --- /dev/null +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffContents.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; + +/** + * 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. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record WorkspaceDiffContents( + /** Complete text before the session first changed the file. Omitted when the file did not exist; an empty string represents an existing empty file. */ + @JsonProperty("before") String before, + /** Complete current text read when computing the diff. Omitted for a deleted file; an empty string represents an existing empty file. */ + @JsonProperty("after") String after +) { +} diff --git a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChange.java b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChange.java index e63b92b569..5ac0ec4efc 100644 --- a/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChange.java +++ b/java/sdk/src/generated/java/com/github/copilot/generated/rpc/WorkspaceDiffFileChange.java @@ -30,6 +30,27 @@ public record WorkspaceDiffFileChange( /** Original file path for renamed files. */ @JsonProperty("oldPath") String oldPath, /** Whether the diff content was omitted because it exceeded the per-file size limit. */ - @JsonProperty("isTruncated") Boolean isTruncated + @JsonProperty("isTruncated") Boolean isTruncated, + /** 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. */ + @JsonProperty("contents") WorkspaceDiffContents contents ) { + + /** + * Creates a record with the components it had before later optional fields were added. + * + * @param path 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). + * @param diff Unified diff content for the file. Empty when the diff was truncated. + * @param changeType Type of change represented by this file diff. + * @param oldPath Original file path for renamed files. + * @param isTruncated Whether the diff content was omitted because it exceeded the per-file size limit. + */ + public WorkspaceDiffFileChange( + String path, + String diff, + WorkspaceDiffFileChangeType changeType, + String oldPath, + Boolean isTruncated + ) { + this(path, diff, changeType, oldPath, isTruncated, null); + } } diff --git a/java/sdk/src/main/java/com/github/copilot/AhpHost.java b/java/sdk/src/main/java/com/github/copilot/AhpHost.java index 9258289c85..92b400a205 100644 --- a/java/sdk/src/main/java/com/github/copilot/AhpHost.java +++ b/java/sdk/src/main/java/com/github/copilot/AhpHost.java @@ -5,6 +5,8 @@ package com.github.copilot; import com.github.copilot.generated.rpc.HostDisposeParams; +import com.github.copilot.generated.rpc.HostListSessionsParams; +import com.github.copilot.generated.rpc.HostListSessionsResult; import com.github.copilot.generated.rpc.HostPublishSessionParams; import com.github.copilot.generated.rpc.HostPublishSessionResult; import com.github.copilot.generated.rpc.HostStartResult; @@ -83,8 +85,8 @@ public CompletableFuture dispose() { } /** - * Publishes an exact resident session attached to the owning client, without - * invoking a factory or transferring session ownership. + * Durably advertises an exact attached session in the compute-scoped catalog, + * without invoking a factory or transferring session ownership. * * @param sessionId * resident session identity @@ -94,6 +96,15 @@ public CompletableFuture publishSession(String session return rpc.publishSession(new HostPublishSessionParams(info.hostId(), sessionId)); } + /** + * Lists all live and dormant catalog sessions advertised by this host. + * + * @return the advertised host sessions + */ + public CompletableFuture listSessions() { + return rpc.listSessions(new HostListSessionsParams(info.hostId())); + } + /** Disposes the listener and waits for acknowledged cleanup. */ @Override public void close() { diff --git a/java/sdk/src/main/java/com/github/copilot/AhpHostManager.java b/java/sdk/src/main/java/com/github/copilot/AhpHostManager.java index afd06e8f0e..7cefadcd5f 100644 --- a/java/sdk/src/main/java/com/github/copilot/AhpHostManager.java +++ b/java/sdk/src/main/java/com/github/copilot/AhpHostManager.java @@ -11,7 +11,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.github.copilot.generated.rpc.HostExitReason; import com.github.copilot.generated.rpc.HostExitedNotification; -import com.github.copilot.generated.rpc.HostStartParams; +import com.github.copilot.generated.rpc.HostStartRequest; import com.github.copilot.generated.rpc.ServerHostApi; import com.github.copilot.rpc.ResumeSessionConfig; import com.github.copilot.rpc.SessionConfig; @@ -99,8 +99,10 @@ CompletableFuture start(ServerHostApi rpc, AhpHostOptions options) { } var snapshot = host.options; return rpc - .start(new HostStartParams(host.id, snapshot.getLocalServer(), snapshot.getGithubEnvironment(), - snapshot.getCreateSession() != null, snapshot.getResumeSession() != null)) + .start(new HostStartRequest(host.id).setComputeId(snapshot.getComputeId()) + .setLocalServer(snapshot.getLocalServer()).setGitHubEnvironment(snapshot.getGithubEnvironment()) + .setSessionFactory(snapshot.getCreateSession() != null) + .setResumeFactory(snapshot.getResumeSession() != null)) .thenApply(info -> new AhpHost(info, rpc)).whenComplete((result, error) -> { if (error != null) { endHost(host, null); diff --git a/java/sdk/src/main/java/com/github/copilot/AhpHostOptions.java b/java/sdk/src/main/java/com/github/copilot/AhpHostOptions.java index 2ee29adab0..5a4ba6b11c 100644 --- a/java/sdk/src/main/java/com/github/copilot/AhpHostOptions.java +++ b/java/sdk/src/main/java/com/github/copilot/AhpHostOptions.java @@ -18,6 +18,7 @@ */ @CopilotExperimental public final class AhpHostOptions { + private String computeId; private HostLocalServerOptions localServer; private HostGitHubEnvironmentOptions githubEnvironment; private Function> createSession; @@ -33,6 +34,7 @@ public AhpHostOptions() { } AhpHostOptions(AhpHostOptions options) { + computeId = options.computeId; localServer = options.localServer; githubEnvironment = options.githubEnvironment; createSession = options.createSession; @@ -41,6 +43,28 @@ public AhpHostOptions() { onExit = options.onExit; } + /** + * Gets the durable catalog's compute identity. + * + * @return the stable compute identity, or {@code null} for the runtime default + */ + public String getComputeId() { + return computeId; + } + + /** + * Selects the same durable catalog for local and Mission Control hosting. Must + * agree with the GitHub environment's compute identity when both are supplied. + * + * @param computeId + * stable identity, or {@code null} for the runtime default + * @return these options + */ + public AhpHostOptions setComputeId(String computeId) { + this.computeId = computeId; + return this; + } + /** * Gets the local WebSocket transport settings. * 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 e742b242e5..92203ab755 100644 --- a/java/sdk/src/main/java/com/github/copilot/CliServerManager.java +++ b/java/sdk/src/main/java/com/github/copilot/CliServerManager.java @@ -14,7 +14,12 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -34,6 +39,7 @@ final class CliServerManager { private static final Logger LOG = Logger.getLogger(CliServerManager.class.getName()); private static final int STDERR_READER_JOIN_TIMEOUT_MS = 5000; + static final int FORCE_KILL_TIMEOUT_SECONDS = 10; private final CopilotClientOptions options; private final StringBuilder stderrBuffer = new StringBuilder(); @@ -132,12 +138,20 @@ ProcessInfo startCliServer() throws IOException, InterruptedException { // Forward stderr to logger in background startStderrReader(process); - Integer detectedPort = null; - if (!options.isUseStdio()) { - detectedPort = waitForPortAnnouncement(process); + try { + Integer detectedPort = null; + if (!options.isUseStdio()) { + detectedPort = waitForPortAnnouncement(process); + } + return new ProcessInfo(process, detectedPort); + } catch (IOException | RuntimeException error) { + try { + cleanupCliProcess(process, captureDescendants(process), true); + } catch (RuntimeException cleanupError) { + error.addSuppressed(cleanupError); + } + throw error; } - - return new ProcessInfo(process, detectedPort); } /** @@ -206,11 +220,98 @@ private Integer waitForPortAnnouncement(Process process) throws IOException { } } - process.destroyForcibly(); throw new IOException("Timeout waiting for CLI to announce port"); } } + static void cleanupCliProcess(Process process, List descendants, boolean forceImmediately) { + if (!process.isAlive() && descendants.stream().noneMatch(ProcessHandle::isAlive)) { + return; + } + try { + if (terminateProcessTree(process, descendants, forceImmediately)) { + return; + } + if (!forceImmediately) { + if (terminateProcessTree(process, descendants, true)) { + return; + } + } + throw new IllegalStateException("CLI process tree did not terminate after shutdown: " + process.pid()); + } catch (InterruptedException error) { + Thread.currentThread().interrupt(); + throw new CompletionException("Interrupted while terminating the CLI process tree", error); + } catch (ExecutionException error) { + throw new CompletionException("Failed to await CLI process tree termination", error.getCause()); + } + } + + static List captureDescendants(Process process) { + return process.descendants().sorted(Comparator.comparingInt(CliServerManager::processDepth).reversed()) + .toList(); + } + + private static int processDepth(ProcessHandle process) { + int depth = 0; + var parent = process.parent(); + while (parent.isPresent()) { + depth++; + parent = parent.get().parent(); + } + return depth; + } + + private static boolean terminateProcessTree(Process process, List descendants, boolean force) + throws InterruptedException, ExecutionException { + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(FORCE_KILL_TIMEOUT_SECONDS); + // Reap deepest children while their parents can still collect their exit. + // Killing the launcher first can orphan zombies in containers without an init. + for (var child : descendants) { + if (child.isAlive()) { + if (force) { + child.destroyForcibly(); + } else { + child.destroy(); + } + waitForProcessExit(child, deadline); + } + } + if (process.isAlive()) { + if (force) { + process.destroyForcibly(); + } else { + process.destroy(); + } + } + return waitForProcessTreeExit(process, descendants, deadline); + } + + private static boolean waitForProcessTreeExit(Process process, List descendants, long deadline) + throws InterruptedException, ExecutionException { + if (!process.waitFor(Math.max(0, deadline - System.nanoTime()), TimeUnit.NANOSECONDS)) { + return false; + } + for (var child : descendants) { + if (!waitForProcessExit(child, deadline)) { + return false; + } + } + return true; + } + + private static boolean waitForProcessExit(ProcessHandle process, long deadline) + throws InterruptedException, ExecutionException { + if (!process.isAlive()) { + return true; + } + try { + process.onExit().get(Math.max(0, deadline - System.nanoTime()), TimeUnit.NANOSECONDS); + return true; + } catch (TimeoutException error) { + return false; + } + } + String getStderrOutput() { synchronized (stderrBuffer) { return stderrBuffer.toString().trim(); 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 167b1b6f36..baccd4ad19 100644 --- a/java/sdk/src/main/java/com/github/copilot/CopilotClient.java +++ b/java/sdk/src/main/java/com/github/copilot/CopilotClient.java @@ -25,6 +25,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import com.fasterxml.jackson.databind.JsonNode; import com.github.copilot.ffi.FfiRuntimeHost; import com.github.copilot.ffi.NativeRuntimeLoader; import com.github.copilot.rpc.CopilotClientMode; @@ -39,7 +40,6 @@ import com.github.copilot.rpc.CreateSessionResponse; import com.github.copilot.generated.rpc.SessionOptionsUpdateParams; import com.github.copilot.generated.rpc.SessionInstalledPlugin; -import com.github.copilot.generated.rpc.ConnectResult; import com.github.copilot.generated.rpc.GitHubTelemetryNotification; import com.github.copilot.generated.rpc.ServerRpc; import com.github.copilot.generated.rpc.SessionEventLogRegisterInterestParams; @@ -99,7 +99,7 @@ public final class CopilotClient implements AutoCloseable { public static final int AUTOCLOSEABLE_TIMEOUT_SECONDS = 10; private static final int RUNTIME_SHUTDOWN_TIMEOUT_SECONDS = 10; private static final int PROCESS_EXIT_TIMEOUT_SECONDS = 10; - private static final int FORCE_KILL_TIMEOUT_SECONDS = 10; + private static final int FORCE_KILL_TIMEOUT_SECONDS = CliServerManager.FORCE_KILL_TIMEOUT_SECONDS; /** * One-shot dispatcher used to run the owned-executor shutdown off any caller @@ -585,9 +585,21 @@ private Connection startCoreBody() { installationConfirmationAdapter.registerHandlers(connectedRpc); } InstallationConfirmationAdapter connectedInstallationConfirmationAdapter = installationConfirmationAdapter; + // JsonRpcClient keeps one handler per method, so this one serves every request + // that honors cancellation. + connectedRpc.registerMethodHandler("$/cancelRequest", (rpcId, params) -> { + dispatcher.handleCancelRequest(params); + if (connectedInstallationConfirmationAdapter != null) { + connectedInstallationConfirmationAdapter.handleCancel(connectedRpc, params); + } + }); connectedRpc.setCloseHandler(() -> { ahpHosts.disconnect(connection.serverRpc().host); - sessions.values().forEach(CopilotSession::cancelPendingExternalTools); + sessions.values().forEach(session -> { + session.cancelPendingExternalTools(); + session.clearSkillProvider(); + }); + dispatcher.cancelSkillProviderCalls(); if (connectedLlmAdapter != null) { connectedLlmAdapter.cancelPending(); } @@ -652,7 +664,11 @@ private Connection startCoreBody() { if (drainBeforeCleanup) { serverManager.awaitStderrReader(); } - cleanupCliProcess(process, true); + try { + CliServerManager.cleanupCliProcess(process, CliServerManager.captureDescendants(process), true); + } catch (RuntimeException cleanupError) { + e.addSuppressed(cleanupError); + } if (!drainBeforeCleanup) { serverManager.awaitStderrReader(); } @@ -704,11 +720,10 @@ private void verifyProtocolVersion(Connection connection) throws Exception { if (clientInfo != null && !clientInfo.isEmpty()) { connectParams.put("clientInfo", clientInfo); } - var connectResponse = connection.rpc.invoke("connect", connectParams, ConnectResult.class).get(30, + var connectResponse = connection.rpc.invoke("connect", connectParams, JsonNode.class).get(30, TimeUnit.SECONDS); - serverVersion = connectResponse.protocolVersion() != null - ? connectResponse.protocolVersion().intValue() - : null; + var protocolVersion = connectResponse.get("protocolVersion"); + serverVersion = protocolVersion != null && !protocolVersion.isNull() ? protocolVersion.intValue() : null; } catch (Exception e) { // Unwrap CompletionException/ExecutionException to check inner cause Throwable cause = e; @@ -754,7 +769,7 @@ private static boolean isUnsupportedConnectMethod(JsonRpcException ex) { *

  • Requests runtime shutdown for SDK-owned CLI processes
  • *
  • Closes stdin for an owned stdio process and waits for its host * cleanup
  • - *
  • Closes the JSON-RPC connection, terminating an owned process if + *
  • Closes the JSON-RPC connection, terminating an owned process tree if * needed
  • * *

    @@ -794,7 +809,8 @@ public CompletableFuture stop() { } /** - * Forces an immediate stop of the client without graceful cleanup. + * Immediately stops the client, retires sessions and pending waits, and drops + * unadmitted callbacks. * * @return A future that completes when the client is stopped */ @@ -802,8 +818,8 @@ public CompletableFuture forceStop() { ahpHosts.disconnect(); disposed = true; var activeSessions = new ArrayList<>(sessions.values()); + activeSessions.forEach(CopilotSession::forceStopLocally); sessions.clear(); - activeSessions.forEach(CopilotSession::cancelPendingExternalTools); gitHubTokenProviders.clear(); // Dispatch the blocking shutdownOwnedExecutor() on a dedicated thread: // cleanupConnection() is chained off async work running on the owned @@ -831,6 +847,10 @@ private CompletableFuture cleanupConnection(boolean gracefulRuntimeShutdow return CompletableFuture.completedFuture(null); } + // Retain children before EOF or shutdown can let their launcher exit. + List descendants = connection.process == null + ? List.of() + : CliServerManager.captureDescendants(connection.process); CompletableFuture shutdownFuture = CompletableFuture.completedFuture(null); if (gracefulRuntimeShutdown && (connection.process != null || connection.runtimeHost != null)) { long runtimeShutdownStartNanos = System.nanoTime(); @@ -850,8 +870,10 @@ private CompletableFuture cleanupConnection(boolean gracefulRuntimeShutdow } return shutdownFuture.handleAsync((ignored, error) -> { + boolean preserveDetachedServices = gracefulRuntimeShutdown && error == null && !options.isUseStdio(); if (gracefulRuntimeShutdown && connection.process != null && options.isUseStdio()) { - awaitStdioProcessExit(connection.process); + boolean exitObserved = awaitOwnedRuntimeExit(connection); + preserveDetachedServices = exitObserved && error == null; } try { connection.rpc.close(); @@ -860,7 +882,11 @@ private CompletableFuture cleanupConnection(boolean gracefulRuntimeShutdow } if (connection.process != null) { - cleanupCliProcess(connection.process, !gracefulRuntimeShutdown || error != null); + // Successful shutdown leaves intentionally detached services alive. + // Keep the captured tree only for failed or forced shutdown. + CliServerManager.cleanupCliProcess(connection.process, + preserveDetachedServices ? List.of() : descendants, + !gracefulRuntimeShutdown || error != null); } if (connection.runtimeHost != null) { closeRuntimeHost(connection.runtimeHost); @@ -870,54 +896,24 @@ private CompletableFuture cleanupConnection(boolean gracefulRuntimeShutdow }).thenCompose(result -> result); } - private static void awaitStdioProcessExit(Process process) { + private static boolean awaitOwnedRuntimeExit(Connection connection) { + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(PROCESS_EXIT_TIMEOUT_SECONDS); try { - // Host telemetry flushes after stdio EOF, not the shutdown RPC response. - // Keep the reader draining stdout until the child has finished. - process.getOutputStream().close(); - if (!process.waitFor(PROCESS_EXIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { - LOG.fine("Process did not exit after stdin EOF within graceful shutdown timeout; terminating"); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOG.log(Level.FINE, "Interrupted while waiting for process exit", e); - } catch (IOException e) { - LOG.log(Level.FINE, "Error closing process stdin", e); - } - } - - /** - * Returns true only when the child had already exited and no streams were - * destroyed. - */ - private static boolean cleanupCliProcess(Process process, boolean forceImmediately) { - try { - if (process.isAlive()) { - if (forceImmediately) { - process.destroyForcibly(); - if (!process.waitFor(FORCE_KILL_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { - LOG.fine("Process did not terminate within force kill timeout"); - } - return false; - } - - process.destroy(); - if (process.waitFor(FORCE_KILL_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { - return false; - } - - process.destroyForcibly(); - if (!process.waitFor(FORCE_KILL_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { - LOG.fine("Process did not terminate within force kill timeout"); - } + // Host telemetry flushes after stdin EOF, not the shutdown response. + connection.process.getOutputStream().close(); + if (!connection.rpc.awaitPeerEof(deadline - System.nanoTime(), TimeUnit.NANOSECONDS)) { + LOG.fine("Runtime transport did not reach EOF within graceful shutdown timeout; terminating tree"); return false; } + if (!connection.process.waitFor(Math.max(0, deadline - System.nanoTime()), TimeUnit.NANOSECONDS)) { + LOG.fine("Launcher did not exit after runtime EOF; terminating launcher"); + } return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); - LOG.log(Level.FINE, "Interrupted while killing process", e); - } catch (Exception e) { - LOG.log(Level.FINE, "Error killing process", e); + LOG.log(Level.FINE, "Interrupted while waiting for process exit", e); + } catch (IOException e) { + LOG.log(Level.FINE, "Error awaiting runtime exit after closing stdin", e); } return false; } @@ -962,6 +958,10 @@ public CompletableFuture createSession(SessionConfig config) { return CompletableFuture.failedFuture( new IllegalArgumentException("gitHubToken and gitHubTokenProvider are mutually exclusive")); } + if (config.getCloud() != null && config.getSkillProvider() != null) { + return CompletableFuture.failedFuture( + new IllegalArgumentException("Skill providers are not supported for cloud sessions.")); + } return ensureConnected().thenCompose(connection -> { long totalNanos = System.nanoTime(); // For cloud sessions, let the CLI/server assign the session id @@ -1030,7 +1030,7 @@ public CompletableFuture createSession(SessionConfig config) { if (options.getMode() == CopilotClientMode.EMPTY) { if (config.getAvailableTools() == null) { if (registeredIdHolder[0] != null) { - sessions.remove(registeredIdHolder[0]); + sessions.remove(registeredIdHolder[0], preRegisteredSessionHolder[0]); } throw new IllegalArgumentException( "CopilotClient is in Mode = EMPTY but the session config did not specify " @@ -1130,9 +1130,10 @@ public CompletableFuture createSession(SessionConfig config) { }).exceptionally(ex -> { if (preRegisteredSessionHolder[0] != null) { preRegisteredSessionHolder[0].cancelPendingExternalTools(); + preRegisteredSessionHolder[0].clearSkillProvider(); } if (registeredIdHolder[0] != null) { - sessions.remove(registeredIdHolder[0]); + sessions.remove(registeredIdHolder[0], preRegisteredSessionHolder[0]); } if (tokenRegistration != null) { tokenRegistration.close(); @@ -1190,6 +1191,14 @@ public CompletableFuture resumeSession(String sessionId, ResumeS } return ensureConnected().thenCompose(connection -> { long totalNanos = System.nanoTime(); + // Validate before registering so a rejected resume leaves any resident + // session and its skill provider in place. + if (options.getMode() == CopilotClientMode.EMPTY && config.getAvailableTools() == null) { + throw new IllegalArgumentException( + "CopilotClient is in Mode = EMPTY but the resume session config did not specify " + + "availableTools. Empty mode requires every session to explicitly opt into " + + "the tools it wants — e.g. setAvailableTools(new ToolSet().addBuiltIn(BuiltInTools.ISOLATED))."); + } // Register the session before the RPC call to avoid missing early events. long setupNanos = System.nanoTime(); CopilotSession replacedSession = sessions.get(sessionId); @@ -1218,15 +1227,8 @@ public CompletableFuture resumeSession(String sessionId, ResumeS request.setEnableGitHubTelemetryForwarding(true); } - // Empty mode: validate availableTools and set toolFilterPrecedence for resume - // path + // Empty mode: set toolFilterPrecedence and isolation defaults for resume path if (options.getMode() == CopilotClientMode.EMPTY) { - if (config.getAvailableTools() == null) { - throw new IllegalArgumentException( - "CopilotClient is in Mode = EMPTY but the resume session config did not specify " - + "availableTools. Empty mode requires every session to explicitly opt into " - + "the tools it wants — e.g. setAvailableTools(new ToolSet().addBuiltIn(BuiltInTools.ISOLATED))."); - } request.setToolFilterPrecedence("excluded"); if (request.getSkipEmbeddingRetrieval() == null) { request.setSkipEmbeddingRetrieval(true); @@ -1291,7 +1293,7 @@ public CompletableFuture resumeSession(String sessionId, ResumeS // re-key. String returnedId = response.sessionId(); if (returnedId != null && !returnedId.equals(sessionId)) { - sessions.remove(sessionId); + sessions.remove(sessionId, session); session.setActiveSessionId(returnedId); sessions.put(returnedId, session); } @@ -1314,15 +1316,11 @@ public CompletableFuture resumeSession(String sessionId, ResumeS }); }).exceptionally(ex -> { session.cancelPendingExternalTools(); - sessions.remove(sessionId); - // Also remove the re-keyed entry if the server returned a different ID - String activeId = session.getSessionId(); - if (!sessionId.equals(activeId)) { - sessions.remove(activeId); - } - if (replacedSession != null) { - sessions.putIfAbsent(sessionId, replacedSession); - } + session.clearSkillProvider(); + // A concurrent resume may own the ID now; undo only this attempt. + sessions.compute(sessionId, + (id, current) -> current == null || current == session ? replacedSession : current); + sessions.remove(session.getSessionId(), session); if (tokenRegistration != null) { tokenRegistration.close(); } @@ -1501,12 +1499,13 @@ CompletableFuture updateSessionOptionsForMode(CopilotSession session, Bool * @see ConnectionState */ public ConnectionState getState() { - if (connectionFuture == null) + CompletableFuture future = connectionFuture; + if (future == null) return ConnectionState.DISCONNECTED; - if (connectionFuture.isCompletedExceptionally()) - return ConnectionState.ERROR; - if (!connectionFuture.isDone()) + if (!future.isDone()) return ConnectionState.CONNECTING; + if (future.isCompletedExceptionally()) + return ConnectionState.ERROR; return ConnectionState.CONNECTED; } @@ -1668,6 +1667,7 @@ public CompletableFuture deleteSession(String sessionId) { } CopilotSession session = sessions.remove(sessionId); if (session != null) { + session.clearSkillProvider(); session.releaseGitHubTokenProviderRegistration(); } })); diff --git a/java/sdk/src/main/java/com/github/copilot/CopilotSession.java b/java/sdk/src/main/java/com/github/copilot/CopilotSession.java index 985050a128..497bce7983 100644 --- a/java/sdk/src/main/java/com/github/copilot/CopilotSession.java +++ b/java/sdk/src/main/java/com/github/copilot/CopilotSession.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -15,6 +16,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; @@ -47,6 +49,7 @@ import com.github.copilot.generated.rpc.SessionPermissionsHandlePendingPermissionRequestParams; import com.github.copilot.generated.rpc.SessionRpc; import com.github.copilot.generated.rpc.SessionToolsHandlePendingToolCallParams; +import com.github.copilot.generated.rpc.SessionToolsSetParams; import com.github.copilot.generated.rpc.SessionUiElicitationParams; import com.github.copilot.generated.rpc.SessionUiHandlePendingElicitationParams; import com.github.copilot.generated.rpc.UIElicitationResponse; @@ -110,6 +113,8 @@ import com.github.copilot.rpc.SessionStartHookInput; import com.github.copilot.rpc.SessionUiApi; import com.github.copilot.rpc.SessionUiCapabilities; +import com.github.copilot.rpc.SubagentStartHookInput; +import com.github.copilot.rpc.SubagentStopHookInput; import com.github.copilot.rpc.ToolDefinition; import com.github.copilot.rpc.ToolResultObject; import com.github.copilot.rpc.TranscriptRecoveryReport; @@ -168,6 +173,7 @@ public final class CopilotSession implements AutoCloseable { private static final Logger LOG = Logger.getLogger(CopilotSession.class.getName()); private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); private final java.util.Set> structuredWaits = ConcurrentHashMap.newKeySet(); + private final Map, CompletableFuture> plainWaits = new ConcurrentHashMap<>(); /** * Fixed name of the runtime's built-in tool-search tool. A client can replace @@ -191,8 +197,10 @@ public final class CopilotSession implements AutoCloseable { private final SessionUiApi ui; private final JsonRpcClient rpc; private volatile SessionRpc sessionRpc; - private final Set> eventHandlers = new CopyOnWriteArraySet<>(); - private final Map toolHandlers = new ConcurrentHashMap<>(); + private final Set eventHandlers = new CopyOnWriteArraySet<>(); + private final AtomicReference> toolHandlers = new AtomicReference<>(Map.of()); + private final Object setToolsLock = new Object(); + private CompletableFuture setToolsTail = CompletableFuture.completedFuture(null); private final Map pendingExternalTools = new ConcurrentHashMap<>(); private boolean externalToolsClosed; private final Map commandHandlers = new ConcurrentHashMap<>(); @@ -205,6 +213,7 @@ public final class CopilotSession implements AutoCloseable { private final AtomicReference exitPlanModeHandler = new AtomicReference<>(); private final AtomicReference autoModeSwitchHandler = new AtomicReference<>(); private final AtomicReference hooksHandler = new AtomicReference<>(); + private final AtomicReference skillProvider = new AtomicReference<>(); private volatile EventErrorHandler eventErrorHandler; private volatile EventErrorPolicy eventErrorPolicy = EventErrorPolicy.PROPAGATE_AND_LOG_ERRORS; private volatile Map>> transformCallbacks; @@ -212,8 +221,17 @@ public final class CopilotSession implements AutoCloseable { private volatile Executor executor; private volatile GitHubTokenProviderRegistry.Registration gitHubTokenProviderRegistration; - /** Tracks whether this session instance has been terminated via close(). */ + /** Tracks whether this session instance has been closed or force-stopped. */ private volatile boolean isTerminated = false; + private volatile boolean callbacksRetired = false; + + private static final class EventSubscription { + private final AtomicReference> handler; + + EventSubscription(Consumer handler) { + this.handler = new AtomicReference<>(handler); + } + } private static final class PendingExternalTool { private static final int WAITING = 0; @@ -654,19 +672,33 @@ public CompletableFuture sendAndWait(MessageOptions optio } }; - Closeable subscription = on(handler); + var result = new CompletableFuture(); + Closeable subscription; + synchronized (this) { + subscription = on(handler); + plainWaits.put(result, future); + } + result.whenComplete((value, error) -> plainWaits.remove(result)); - send(options).exceptionally(ex -> { + try { + send(options).exceptionally(ex -> { + try { + subscription.close(); + } catch (IOException e) { + LOG.log(Level.SEVERE, "Error closing subscription", e); + } + future.completeExceptionally(ex); + return null; + }); + } catch (RuntimeException error) { + plainWaits.remove(result); try { subscription.close(); - } catch (Exception e) { - LOG.log(Level.SEVERE, "Error closing subscription", e); + } catch (IOException closeError) { + error.addSuppressed(closeError); } - future.completeExceptionally(ex); - return null; - }); - - var result = new CompletableFuture(); + throw error; + } // Schedule timeout on the shared session-level scheduler. // Per Javadoc, timeoutMs <= 0 means "no timeout". @@ -685,7 +717,10 @@ public CompletableFuture sendAndWait(MessageOptions optio } catch (IOException closeEx) { e.addSuppressed(closeEx); } - result.completeExceptionally(e); + // Retirement owns completion when it has already shut down the scheduler. + if (!isTerminated) { + result.completeExceptionally(e); + } return result; } } @@ -753,6 +788,73 @@ public CompletableFuture sendAndWait(MessageOptions optio return sendAndWait(options, 60000); } + /** + * Replaces this client's externally implemented tools on the live session. + *

    + * This is a complete replacement for the tools supplied by this client only: + * built-in tools, MCP/plugin tools, and tools supplied by other connections are + * not changed. Pass the same {@link ToolDefinition} values accepted by + * {@link com.github.copilot.rpc.SessionConfig#setTools(List)} and + * {@link com.github.copilot.rpc.ResumeSessionConfig#setTools(List)}. Passing an + * empty list removes all tools supplied by this client. + *

    + * The local handlers switch once the runtime accepts the replacement. Tool + * calls already running finish on the handlers that started them. If the + * runtime rejects the replacement, the previous handlers remain unchanged and + * the returned future completes exceptionally. Concurrent replacements on the + * same session are applied in call order. Cancelling the returned future while + * an earlier replacement is still in flight sends nothing; once the request is + * sent, an accepted replacement still installs its handlers unless the session + * closes first. Closing the session rejects queued replacements and prevents + * late acknowledgements from restoring retired handlers. + *

    + * The agent sees the new tools from its next model request, which can fall + * within a turn in progress. A model request already in flight was made with + * the previous tools, so the agent can still call a tool you removed. This + * session doesn't answer that call, and it can stay pending until the turn is + * aborted. If a running turn might still call a tool you remove, replace tools + * while the session is idle. + * + * @param tools + * the complete replacement tool list for this client + * @return a future that completes when the runtime accepts the replacement and + * the local handler registry has been updated + */ + @CopilotExperimental + public CompletableFuture setTools(List tools) { + ensureNotTerminated(); + List toolList = tools != null ? List.copyOf(tools) : List.of(); + Map replacementHandlers = buildToolHandlerMap(toolList); + var params = new SessionToolsSetParams(sessionId, SessionRequestBuilder.toProtocolExternalTools(toolList)); + + var result = new CompletableFuture(); + CompletableFuture operation; + synchronized (setToolsLock) { + operation = setToolsTail.handle((ignored, error) -> null).thenCompose(ignored -> { + if (result.isCancelled()) { + return CompletableFuture.completedFuture(null); + } + ensureNotTerminated(); + return getRpc().tools.set(params).thenRun(() -> { + synchronized (CopilotSession.this) { + ensureNotTerminated(); + toolHandlers.set(replacementHandlers); + } + }); + }); + setToolsTail = operation.handle((ignored, error) -> null); + } + + operation.whenComplete((value, error) -> { + if (error != null) { + result.completeExceptionally(error); + } else { + result.complete(null); + } + }); + return result; + } + /** * Sends a prompt using the schema generated for a {@link CopilotResponse} type. * @@ -871,14 +973,17 @@ synchronized void event(SessionEvent event) { } } var state = new State(); - Closeable subscription = on(event -> { - if (event instanceof AssistantMessageEvent || event instanceof SessionIdleEvent - || event instanceof SessionErrorEvent - || event instanceof com.github.copilot.generated.UserMessageEvent) { - state.event(event); - } - }); - structuredWaits.add(result); + Closeable subscription; + synchronized (this) { + subscription = on(event -> { + if (event instanceof AssistantMessageEvent || event instanceof SessionIdleEvent + || event instanceof SessionErrorEvent + || event instanceof com.github.copilot.generated.UserMessageEvent) { + state.event(event); + } + }); + structuredWaits.add(result); + } ScheduledFuture timer; try { timer = timeoutMs > 0 @@ -957,9 +1062,20 @@ synchronized void event(SessionEvent event) { * @see #setEventErrorPolicy(EventErrorPolicy) */ public Closeable on(Consumer handler) { - ensureNotTerminated(); - eventHandlers.add(handler); - return () -> eventHandlers.remove(handler); + java.util.Objects.requireNonNull(handler, "handler"); + EventSubscription subscription; + synchronized (this) { + ensureNotTerminated(); + subscription = eventHandlers.stream().filter(entry -> handler.equals(entry.handler.get())).findFirst() + .orElseGet(() -> new EventSubscription(handler)); + eventHandlers.add(subscription); + } + return () -> { + synchronized (this) { + subscription.handler.set(null); + eventHandlers.remove(subscription); + } + }; } /** @@ -1015,8 +1131,7 @@ public Closeable on(Class eventType, Consumer han handler.accept(eventType.cast(event)); } }; - eventHandlers.add(wrapper); - return () -> eventHandlers.remove(wrapper); + return on(wrapper); } /** @@ -1043,12 +1158,25 @@ public Closeable on(Class eventType, Consumer han * @see #setEventErrorPolicy(EventErrorPolicy) */ void dispatchEvent(SessionEvent event) { + if (callbacksRetired) { + return; + } // Handle broadcast request events (protocol v3) and passive in-memory state // updates (capabilities, open-canvases snapshot) before dispatching to user // handlers. Fire-and-forget: any RPC response is sent asynchronously. handleBroadcastEventAsync(event); - for (Consumer handler : eventHandlers) { + for (EventSubscription subscription : eventHandlers) { + Consumer handler; + synchronized (this) { + if (callbacksRetired) { + return; + } + handler = subscription.handler.get(); + } + if (handler == null) { + continue; + } try { handler.accept(event); } catch (Exception e) { @@ -1098,7 +1226,9 @@ private void handleBroadcastEventAsync(SessionEvent event) { if (tool == null) { return; // This client doesn't handle this tool; another client will } - executeToolAndRespondAsync(data.requestId(), data.toolName(), data.toolCallId(), data.arguments(), tool); + String agentId = toolEvent.getAgentId(); + executeToolAndRespondAsync(data.requestId(), data.toolName(), data.toolCallId(), data.arguments(), tool, + agentId == null || agentId.isEmpty() ? null : agentId); } else if (event instanceof ExternalToolCompletedEvent completedEvent) { var data = completedEvent.getData(); @@ -1205,7 +1335,7 @@ void populateToolSearchMetadata(String toolName, com.github.copilot.rpc.ToolInvo * {@code session.tools.handlePendingToolCall}. */ private void executeToolAndRespondAsync(String requestId, String toolName, String toolCallId, Object arguments, - ToolDefinition tool) { + ToolDefinition tool, String agentId) { var pending = new PendingExternalTool(); synchronized (this) { if (isTerminated || externalToolsClosed || pendingExternalTools.putIfAbsent(requestId, pending) != null) { @@ -1219,6 +1349,7 @@ private void executeToolAndRespondAsync(String requestId, String toolName, Strin : (arguments != null ? MAPPER.valueToTree(arguments) : null); var invocation = new com.github.copilot.rpc.ToolInvocation().setSessionId(sessionId) .setToolCallId(toolCallId).setToolName(toolName).setArguments(argumentsNode); + invocation.setAgentId(agentId).setRequestId(requestId); if (TOOL_SEARCH_TOOL_NAME.equals(toolName)) { try { @@ -1440,12 +1571,18 @@ private void sendMcpAuthResponse(String requestId, McpAuthResult result) { * the list of tool definitions with handlers */ void registerTools(List tools) { - toolHandlers.clear(); - if (tools != null) { - for (ToolDefinition tool : tools) { - toolHandlers.put(tool.name(), tool); - } + toolHandlers.set(buildToolHandlerMap(tools)); + } + + private static Map buildToolHandlerMap(List tools) { + if (tools == null || tools.isEmpty()) { + return Map.of(); } + Map handlers = new LinkedHashMap<>(); + for (ToolDefinition tool : tools) { + handlers.put(tool.name(), tool); + } + return Collections.unmodifiableMap(handlers); } /** @@ -1679,7 +1816,7 @@ public CompletableFuture input(String message, InputOptions options) { * @return the tool definition, or {@code null} if not found */ ToolDefinition getTool(String name) { - return toolHandlers.get(name); + return toolHandlers.get().get(name); } /** @@ -1703,6 +1840,18 @@ void registerMcpAuthHandler(McpAuthHandler handler) { mcpAuthHandler.set(handler); } + void registerSkillProvider(SkillProvider provider) { + skillProvider.set(provider); + } + + SkillProvider getSkillProvider() { + return skillProvider.get(); + } + + void clearSkillProvider() { + skillProvider.set(null); + } + /** * Handles a permission request from the Copilot CLI. *

    @@ -2224,6 +2373,26 @@ CompletableFuture handleHooksInvoke(String hookType, JsonNode input) { return stopResult.thenApply(output -> (Object) output); } break; + case "subagentStart" : + if (hooks.getOnSubagentStart() != null) { + SubagentStartHookInput startInput = MAPPER.treeToValue(input, SubagentStartHookInput.class); + var startResult = hooks.getOnSubagentStart().handle(startInput, invocation); + if (startResult == null) { + return CompletableFuture.completedFuture(null); + } + return startResult.thenApply(output -> (Object) output); + } + break; + case "subagentStop" : + if (hooks.getOnSubagentStop() != null) { + SubagentStopHookInput stopInput = MAPPER.treeToValue(input, SubagentStopHookInput.class); + var stopResult = hooks.getOnSubagentStop().handle(stopInput, invocation); + if (stopResult == null) { + return CompletableFuture.completedFuture(null); + } + return stopResult.thenApply(output -> (Object) output); + } + break; default : LOG.fine("Unhandled hook type: " + hookType); } @@ -2711,23 +2880,16 @@ private void ensureNotTerminated() { *

    * This destroys the session on the server, clears all event handlers, and * releases tool and permission handlers. After calling this method, the session - * cannot be used again. Subsequent calls to this method have no effect. + * cannot be used again. Subsequent calls to this method have no effect. Pending + * response waits fail when the session retires; ordinary callbacks remain + * active until the detach acknowledgement. */ @Override public void close() { - synchronized (this) { - if (isTerminated) { - return; // Already terminated - no-op - } - isTerminated = true; + if (!retire(false)) { + return; } - structuredWaits.forEach(wait -> wait - .completeExceptionally(new IllegalStateException("Session closed before structured output completed"))); - cancelPendingExternalTools(); - timeoutScheduler.shutdownNow(); - releaseGitHubTokenProviderRegistration(); - RuntimeException detachFailure = null; try { SessionDetachResponse response = rpc @@ -2742,21 +2904,94 @@ public void close() { Thread.currentThread().interrupt(); } detachFailure = new IllegalStateException("Failed to detach session " + sessionId, e); + } finally { + retireCallbacks(); + } + + if (detachFailure != null) { + throw detachFailure; } + } + /** + * Retires a tracked session without an RPC or joining admitted user handlers. + */ + void forceStopLocally() { + retire(true); + } + + private boolean retire(boolean force) { + List waitCompletions = new ArrayList<>(); + synchronized (this) { + if (isTerminated) { + if (force) { + retireCallbacks(); + } + return false; + } + isTerminated = true; + structuredWaits.forEach(wait -> waitCompletions.add(() -> wait + .completeExceptionally(new IllegalStateException("Session closed before response completed")))); + plainWaits.forEach((result, terminal) -> { + if (terminal.isDone()) { + // Preserve a received terminal outcome even if its scheduler handoff is queued. + waitCompletions.add(() -> terminal.whenComplete((value, error) -> { + if (error != null) { + result.completeExceptionally(error); + } else { + result.complete(value); + } + })); + } else { + waitCompletions.add(() -> result.completeExceptionally( + new IllegalStateException("Session closed before response completed"))); + } + }); + structuredWaits.clear(); + if (force) { + retireCallbacks(); + } + plainWaits.clear(); + } + + cancelPendingExternalTools(); + clearSkillProvider(); + timeoutScheduler.shutdownNow(); + releaseGitHubTokenProviderRegistration(); + if (!waitCompletions.isEmpty()) { + // Completion can run blocking user continuations; don't borrow a shared or user + // executor. + var completions = Executors.newCachedThreadPool(task -> { + var thread = new Thread(task, "copilot-session-retirement"); + thread.setDaemon(true); + return thread; + }); + try { + waitCompletions.forEach(completions::execute); + } finally { + completions.shutdown(); + } + } + return true; + } + + private synchronized void retireCallbacks() { + callbacksRetired = true; + eventHandlers.forEach(subscription -> subscription.handler.set(null)); eventHandlers.clear(); - toolHandlers.clear(); + toolHandlers.set(Map.of()); commandHandlers.clear(); + bearerTokenProviders.clear(); permissionHandler.set(null); + mcpAuthHandler.set(null); userInputHandler.set(null); elicitationHandler.set(null); exitPlanModeHandler.set(null); autoModeSwitchHandler.set(null); hooksHandler.set(null); - - if (detachFailure != null) { - throw detachFailure; - } + skillProvider.set(null); + eventErrorHandler = null; + transformCallbacks = null; } // ===== Internal response types for agent API ===== diff --git a/java/sdk/src/main/java/com/github/copilot/InstallationConfirmationAdapter.java b/java/sdk/src/main/java/com/github/copilot/InstallationConfirmationAdapter.java index 57d5ab26fe..99eddbe545 100644 --- a/java/sdk/src/main/java/com/github/copilot/InstallationConfirmationAdapter.java +++ b/java/sdk/src/main/java/com/github/copilot/InstallationConfirmationAdapter.java @@ -47,7 +47,6 @@ final class InstallationConfirmationAdapter { void registerHandlers(JsonRpcClient rpc) { rpc.registerMethodHandler("installations.confirm", (rpcId, params) -> handleConfirm(rpc, rpcId, params)); - rpc.registerMethodHandler("$/cancelRequest", (rpcId, params) -> handleCancel(rpc, params)); } void closePending() { @@ -80,7 +79,10 @@ private void handleConfirm(JsonRpcClient rpc, String rpcId, JsonNode params) { } } - private void handleCancel(JsonRpcClient rpc, JsonNode params) { + /** + * Cancels the confirmation named by a {@code $/cancelRequest} notification. + */ + void handleCancel(JsonRpcClient rpc, JsonNode params) { if (params == null || !params.has("id") || !params.get("id").canConvertToLong() || !params.get("id").isIntegralNumber()) { return; diff --git a/java/sdk/src/main/java/com/github/copilot/JsonRpcClient.java b/java/sdk/src/main/java/com/github/copilot/JsonRpcClient.java index fc10946829..310733aab8 100644 --- a/java/sdk/src/main/java/com/github/copilot/JsonRpcClient.java +++ b/java/sdk/src/main/java/com/github/copilot/JsonRpcClient.java @@ -14,8 +14,10 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -53,6 +55,8 @@ class JsonRpcClient implements AutoCloseable { private final Map> pendingRequests = new ConcurrentHashMap<>(); private final Map> notificationHandlers = new ConcurrentHashMap<>(); private final ExecutorService readerExecutor; + private final CountDownLatch readerExited = new CountDownLatch(1); + private boolean peerEof; private final Object closeHandlerLock = new Object(); private volatile boolean running = true; private boolean closeNotified; @@ -213,12 +217,20 @@ public void sendResponse(Object id, Object result) throws IOException { * Sends a JSON-RPC error response to a server request. */ public void sendErrorResponse(Object id, int code, String message) throws IOException { + sendErrorResponse(id, code, message, null); + } + + /** + * Sends a JSON-RPC error response to a server request with optional data. + */ + public void sendErrorResponse(Object id, int code, String message, Object data) throws IOException { var response = new JsonRpcResponse(); response.setJsonrpc("2.0"); response.setId(id); var error = new JsonRpcError(); error.setCode(code); error.setMessage(message); + error.setData(data); response.setError(error); sendMessage(response); } @@ -284,6 +296,7 @@ private void startReader() { while (inHeaders) { int b = bis.read(); if (b == -1) { + peerEof = true; return; } @@ -319,6 +332,7 @@ private void startReader() { while (read < contentLength) { int result = bis.read(buffer, read, contentLength - read); if (result == -1) { + peerEof = true; return; } read += result; @@ -334,11 +348,19 @@ private void startReader() { LOG.log(Level.SEVERE, "Error in JSON-RPC reader", e); } } finally { - notifyClose(); + try { + notifyClose(); + } finally { + readerExited.countDown(); + } } }); } + boolean awaitPeerEof(long timeout, TimeUnit unit) throws InterruptedException { + return readerExited.await(Math.max(0, timeout), unit) && peerEof; + } + void setCloseHandler(Runnable handler) { boolean runNow; synchronized (closeHandlerLock) { diff --git a/java/sdk/src/main/java/com/github/copilot/RpcHandlerDispatcher.java b/java/sdk/src/main/java/com/github/copilot/RpcHandlerDispatcher.java index 0dd8a5dc98..888ba538da 100644 --- a/java/sdk/src/main/java/com/github/copilot/RpcHandlerDispatcher.java +++ b/java/sdk/src/main/java/com/github/copilot/RpcHandlerDispatcher.java @@ -8,9 +8,14 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Map; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -48,6 +53,7 @@ *

  • Lifecycle events
  • * */ +@AllowCopilotExperimental final class RpcHandlerDispatcher { private static final Logger LOG = Logger.getLogger(RpcHandlerDispatcher.class.getName()); @@ -57,6 +63,7 @@ final class RpcHandlerDispatcher { private final LifecycleEventDispatcher lifecycleDispatcher; private final Executor executor; private final GitHubTokenProviderRegistry gitHubTokenProviders; + private final Map> skillProviderCancellations = new ConcurrentHashMap<>(); /** * Creates a dispatcher with session registry and lifecycle dispatcher. @@ -101,6 +108,144 @@ void registerHandlers(JsonRpcClient rpc) { (requestId, params) -> handleProviderTokenGetToken(rpc, requestId, params)); rpc.registerMethodHandler("gitHubToken.getToken", (requestId, params) -> handleGitHubTokenGetToken(rpc, requestId, params)); + rpc.registerMethodHandler("skillProvider.list", + (requestId, params) -> handleSkillProviderList(rpc, requestId, params)); + rpc.registerMethodHandler("skillProvider.read", + (requestId, params) -> handleSkillProviderRead(rpc, requestId, params)); + } + + // The generated skillProvider.* records are package-private because the + // methods are internal, so these handlers read params as JSON. + private void handleSkillProviderList(JsonRpcClient rpc, String requestId, JsonNode params) { + handleSkillProviderRequest(rpc, requestId, params, "skillProvider.list", "listSkills", + (provider, request) -> provider.listSkills(), + skills -> Map.of("skills", skills != null ? skills : Collections.emptyList())); + } + + private void handleSkillProviderRead(JsonRpcClient rpc, String requestId, JsonNode params) { + handleSkillProviderRequest(rpc, requestId, params, "skillProvider.read", "readSkill", + (provider, request) -> provider.readSkill(textParam(request, "name")), markdown -> { + // Write an explicit null: the runtime requires the field and reads null as "not + // found". + var result = MAPPER.createObjectNode(); + if (markdown == null) { + result.putNull("markdown"); + } else { + result.put("markdown", markdown); + } + return result; + }); + } + + private static String textParam(JsonNode params, String field) { + JsonNode value = params != null ? params.get(field) : null; + return value != null && !value.isNull() ? value.asText() : null; + } + + private void handleSkillProviderRequest(JsonRpcClient rpc, String requestId, JsonNode params, String method, + String operation, BiFunction> call, + Function toResult) { + final long requestIdLong = parseRequestId(requestId, method); + if (requestIdLong == -1) { + return; + } + // Registered on the reader thread so a later $/cancelRequest always finds it. + var cancellation = new CompletableFuture(); + skillProviderCancellations.put(requestIdLong, cancellation); + runAsync(() -> { + String failure = "Skill provider " + operation + " failed"; + String sessionId = null; + try { + sessionId = textParam(params, "sessionId"); + SkillProvider provider = findSkillProvider(sessionId); + if (provider == null) { + skillProviderCancellations.remove(requestIdLong, cancellation); + sendSkillProviderError(rpc, requestIdLong, "No skill provider for session: " + sessionId, sessionId, + null); + return; + } + CompletableFuture future = cancellation.isDone() + ? CompletableFuture.failedFuture(new CancellationException()) + : call.apply(provider, params); + if (future == null) { + future = CompletableFuture + .failedFuture(new IllegalStateException(operation + " returned a null future")); + } + final CompletableFuture providerFuture = future; + final String callSessionId = sessionId; + cancellation.thenRun(() -> providerFuture.cancel(true)); + providerFuture.whenComplete((value, error) -> { + skillProviderCancellations.remove(requestIdLong, cancellation); + if (cancellation.isDone()) { + sendSkillProviderCancelled(rpc, requestIdLong); + } else if (error != null) { + sendSkillProviderError(rpc, requestIdLong, failure, callSessionId, error); + } else { + sendSkillProviderResult(rpc, requestIdLong, method, failure, callSessionId, + () -> toResult.apply(value)); + } + }); + } catch (Exception error) { + skillProviderCancellations.remove(requestIdLong, cancellation); + sendSkillProviderError(rpc, requestIdLong, failure, sessionId, error); + } + }); + } + + /** + * Cancels the skill provider call named by a {@code $/cancelRequest} + * notification, if it is still in flight. + */ + void handleCancelRequest(JsonNode params) { + JsonNode id = params != null ? params.get("id") : null; + if (id == null || !id.isIntegralNumber() || !id.canConvertToLong()) { + return; + } + CompletableFuture cancellation = skillProviderCancellations.get(id.asLong()); + if (cancellation != null) { + cancellation.complete(null); + } + } + + /** Cancels every in-flight skill provider call when the connection closes. */ + void cancelSkillProviderCalls() { + skillProviderCancellations.values().forEach(cancellation -> cancellation.complete(null)); + } + + private SkillProvider findSkillProvider(String sessionId) { + CopilotSession session = sessionId != null ? sessions.get(sessionId) : null; + return session != null ? session.getSkillProvider() : null; + } + + private void sendSkillProviderResult(JsonRpcClient rpc, long requestId, String method, String failure, + String sessionId, Supplier result) { + try { + rpc.sendResponse(requestId, result.get()); + } catch (IOException error) { + LOG.log(Level.SEVERE, "Error sending " + method + " response", error); + } catch (RuntimeException error) { + sendSkillProviderError(rpc, requestId, failure, sessionId, error); + } + } + + private void sendSkillProviderCancelled(JsonRpcClient rpc, long requestId) { + try { + rpc.sendErrorResponse(requestId, -32800, "Request cancelled"); + } catch (IOException error) { + LOG.log(Level.FINE, "Error sending skill provider cancellation", error); + } + } + + private void sendSkillProviderError(JsonRpcClient rpc, long requestId, String message, String sessionId, + Throwable error) { + if (error != null) { + LOG.log(Level.WARNING, message + " (sessionId=" + sessionId + ")", error); + } + try { + rpc.sendErrorResponse(requestId, -32603, message); + } catch (IOException sendError) { + LOG.log(Level.SEVERE, "Error sending skill provider error", sendError); + } } private void handleGitHubTokenGetToken(JsonRpcClient rpc, String requestId, JsonNode params) { 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 b2b85cff46..71cf2fcded 100644 --- a/java/sdk/src/main/java/com/github/copilot/SessionRequestBuilder.java +++ b/java/sdk/src/main/java/com/github/copilot/SessionRequestBuilder.java @@ -11,6 +11,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Function; +import com.fasterxml.jackson.core.type.TypeReference; import com.github.copilot.rpc.CopilotClientMode; import com.github.copilot.rpc.CreateSessionRequest; import com.github.copilot.rpc.ProviderConfig; @@ -23,6 +24,10 @@ import com.github.copilot.rpc.SectionOverrideAction; import com.github.copilot.rpc.SessionConfig; import com.github.copilot.rpc.SystemMessageConfig; +import com.github.copilot.rpc.ToolDefinition; +import com.github.copilot.rpc.ToolDefer; +import com.github.copilot.generated.rpc.ProtocolExternalToolDefer; +import com.github.copilot.generated.rpc.ProtocolExternalToolDefinition; /** * Builds JSON-RPC request objects from session configuration. @@ -89,6 +94,39 @@ static ExtractedTransforms extractTransformCallbacks(SystemMessageConfig systemM return new ExtractedTransforms(wireConfig, callbacks); } + static List toProtocolExternalTools(List tools) { + if (tools == null) { + return null; + } + return tools.stream().map(SessionRequestBuilder::toProtocolExternalTool).toList(); + } + + @SuppressWarnings("unchecked") + private static ProtocolExternalToolDefinition toProtocolExternalTool(ToolDefinition tool) { + Map parameters = null; + if (tool.parameters() instanceof Map map) { + parameters = (Map) map; + } else if (tool.parameters() != null) { + parameters = JsonRpcClient.getObjectMapper().convertValue(tool.parameters(), + new TypeReference>() { + }); + } + return new ProtocolExternalToolDefinition(tool.name(), tool.description() != null ? tool.description() : "", + null, parameters, tool.overridesBuiltInTool(), tool.skipPermission(), toProtocolDefer(tool.defer()), + tool.isTerminal(), tool.metadata()); + } + + private static ProtocolExternalToolDefer toProtocolDefer(ToolDefer defer) { + if (defer == null || defer == ToolDefer.NONE) { + return null; + } + return switch (defer) { + case AUTO -> ProtocolExternalToolDefer.AUTO; + case NEVER -> ProtocolExternalToolDefer.NEVER; + case NONE -> null; + }; + } + /** * Builds a CreateSessionRequest from the given configuration. * @@ -162,6 +200,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setPluginDirectories(config.getPluginDirectories()); request.setLargeOutput(config.getLargeOutput()); request.setToolSearch(config.getToolSearch()); + request.setImageGeneration(config.getImageGeneration()); request.setMemory(config.getMemory()); request.setDisabledSkills(config.getDisabledSkills()); request.setDisabledMcpServers(config.getDisabledMcpServers()); @@ -177,6 +216,9 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess config.getEnableHostGitOperations().ifPresent(request::setEnableHostGitOperations); config.getEnableSessionStore().ifPresent(request::setEnableSessionStore); config.getEnableSkills().ifPresent(request::setEnableSkills); + if (config.getSkillProvider() != null) { + request.setHasSkillProvider(true); + } if (config.getEmbeddingCacheStorage() != null) { request.setEmbeddingCacheStorage(config.getEmbeddingCacheStorage()); } @@ -207,6 +249,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setFeatureFlags(config.getFeatureFlags()); request.setExpAssignments(config.getExpAssignments()); config.getEnableManagedSettings().ifPresent(request::setEnableManagedSettings); + config.getEnforceManagedModelDefaults().ifPresent(request::setEnforceManagedModelDefaults); request.setManagedSettings(config.getManagedSettings()); return request; @@ -297,6 +340,9 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo config.getEnableHostGitOperations().ifPresent(request::setEnableHostGitOperations); config.getEnableSessionStore().ifPresent(request::setEnableSessionStore); config.getEnableSkills().ifPresent(request::setEnableSkills); + if (config.getSkillProvider() != null) { + request.setHasSkillProvider(true); + } if (config.getEmbeddingCacheStorage() != null) { request.setEmbeddingCacheStorage(config.getEmbeddingCacheStorage()); } @@ -321,6 +367,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setPluginDirectories(config.getPluginDirectories()); request.setLargeOutput(config.getLargeOutput()); request.setToolSearch(config.getToolSearch()); + request.setImageGeneration(config.getImageGeneration()); request.setMemory(config.getMemory()); request.setDisabledSkills(config.getDisabledSkills()); request.setDisabledMcpServers(config.getDisabledMcpServers()); @@ -351,6 +398,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setFeatureFlags(config.getFeatureFlags()); request.setExpAssignments(config.getExpAssignments()); config.getEnableManagedSettings().ifPresent(request::setEnableManagedSettings); + config.getEnforceManagedModelDefaults().ifPresent(request::setEnforceManagedModelDefaults); request.setManagedSettings(config.getManagedSettings()); return request; @@ -397,6 +445,9 @@ static void configureSession(CopilotSession session, SessionConfig config) { if (config.getOnUserInputRequest() != null) { session.registerUserInputHandler(config.getOnUserInputRequest()); } + if (config.getSkillProvider() != null) { + session.registerSkillProvider(config.getSkillProvider()); + } if (config.getHooks() != null) { session.registerHooks(config.getHooks()); } @@ -449,6 +500,9 @@ static void configureSession(CopilotSession session, ResumeSessionConfig config) if (config.getOnUserInputRequest() != null) { session.registerUserInputHandler(config.getOnUserInputRequest()); } + if (config.getSkillProvider() != null) { + session.registerSkillProvider(config.getSkillProvider()); + } if (config.getHooks() != null) { session.registerHooks(config.getHooks()); } diff --git a/java/sdk/src/main/java/com/github/copilot/SkillProvider.java b/java/sdk/src/main/java/com/github/copilot/SkillProvider.java new file mode 100644 index 0000000000..d2907113f2 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/SkillProvider.java @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * Provides session-scoped skills to the Copilot runtime. + *

    + * A skill provider is registered through {@code SessionConfig} or + * {@code ResumeSessionConfig}. The runtime may invoke provider callbacks + * concurrently, so implementations should be thread-safe and avoid relying on + * callback ordering. + *

    + * When the runtime cancels a call, for example after its time limit or when the + * session disconnects, the SDK cancels the returned future with + * {@link CompletableFuture#cancel(boolean) cancel(true)} and the runtime + * ignores any later result. Cancellation does not interrupt running work; + * observe it with {@link CompletableFuture#isCancelled()} or a completion + * callback to stop early. + * + * @apiNote This API is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +public interface SkillProvider { + + /** + * Lists the skills currently available for the session. + * + * @return a future resolving to the provider's skill descriptors + */ + CompletableFuture> listSkills(); + + /** + * Reads the Markdown content for one skill. + * + * @param name + * the skill invocation name + * @return a future resolving to the skill Markdown, or {@code null} when the + * skill is not found + */ + CompletableFuture readSkill(String name); +} diff --git a/java/sdk/src/main/java/com/github/copilot/SkillProviderDescriptor.java b/java/sdk/src/main/java/com/github/copilot/SkillProviderDescriptor.java new file mode 100644 index 0000000000..04ac45845d --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/SkillProviderDescriptor.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Catalog metadata for one skill supplied by a {@link SkillProvider}. + *

    + * The runtime lists these descriptors without fetching skill content, then + * calls {@link SkillProvider#readSkill(String)} when the skill is used. + * + * @param name + * the invocation and display name + * @param description + * the description shown in skill catalogs + * @param userInvocable + * whether users may invoke the skill directly, or {@code null} for + * the default ({@code true}) + * @param disableModelInvocation + * whether model invocation is disabled, or {@code null} for the + * default ({@code false}) + * @param argumentHint + * an optional freeform argument hint for slash-command catalogs + * @apiNote This API is experimental and may change in a future version. + * @since 1.0.0 + */ +@CopilotExperimental +@JsonInclude(JsonInclude.Include.NON_NULL) +public record SkillProviderDescriptor(@JsonProperty("name") String name, + @JsonProperty("description") String description, @JsonProperty("userInvocable") Boolean userInvocable, + @JsonProperty("disableModelInvocation") Boolean disableModelInvocation, + @JsonProperty("argumentHint") String argumentHint) { +} diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/AutoTier.java b/java/sdk/src/main/java/com/github/copilot/rpc/AutoTier.java index 092dc73026..cfeda84458 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/AutoTier.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/AutoTier.java @@ -12,26 +12,26 @@ * * @see CapiSessionOptions#setAutoTier(AutoTier) */ -public enum AutoTier { +public final class AutoTier { /** Prioritize efficiency. */ - EFFICIENCY("efficiency"), + public static final AutoTier EFFICIENCY = new AutoTier("efficiency"); /** Balance efficiency and intelligence. */ - BALANCE("balance"), + public static final AutoTier BALANCE = new AutoTier("balance"); /** Prioritize intelligence. */ - INTELLIGENCE("intelligence"), + public static final AutoTier INTELLIGENCE = new AutoTier("intelligence"); /** * Integrator-only preset that optimizes for latency. Not a first-party GitHub * Copilot product preference. */ - FAST("fast"); + public static final AutoTier FAST = new AutoTier("fast"); private final String value; - AutoTier(String value) { + private AutoTier(String value) { this.value = value; } @@ -52,7 +52,7 @@ public String getValue() { * the JSON string value * @return the matching tier, or {@code null} if value is {@code null} * @throws IllegalArgumentException - * if the value does not match a known routing tier + * if the value is not a routing identifier */ @JsonCreator public static AutoTier fromValue(String value) { @@ -64,6 +64,26 @@ public static AutoTier fromValue(String value) { return tier; } } - throw new IllegalArgumentException("Unknown AutoTier value: " + value); + return new AutoTier(com.github.copilot.generated.rpc.AutoTier.fromValue(value).getValue()); + } + + /** Returns the known convenience values. @return known routing tiers */ + public static AutoTier[] values() { + return new AutoTier[]{EFFICIENCY, BALANCE, INTELLIGENCE, FAST}; + } + + @Override + public boolean equals(Object other) { + return other instanceof AutoTier tier && value.equals(tier.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value; } } 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 d8ab62af95..fa5e7d3e16 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 @@ -159,6 +159,9 @@ public final class CreateSessionRequest { @JsonProperty("toolSearch") private ToolSearchConfig toolSearch; + @JsonProperty("imageGeneration") + private ImageGenerationConfig imageGeneration; + @JsonProperty("memory") private MemoryConfiguration memory; @@ -205,6 +208,10 @@ public final class CreateSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableSkills; + @JsonProperty("hasSkillProvider") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean hasSkillProvider; + @JsonProperty("embeddingCacheStorage") @JsonInclude(JsonInclude.Include.NON_NULL) private String embeddingCacheStorage; @@ -256,6 +263,10 @@ public final class CreateSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableManagedSettings; + @JsonProperty("enforceManagedModelDefaults") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enforceManagedModelDefaults; + @JsonProperty("managedSettings") @JsonInclude(JsonInclude.Include.NON_NULL) private ManagedSettings managedSettings; @@ -749,6 +760,19 @@ public ToolSearchConfig getToolSearch() { return toolSearch; } + /** Gets image generation config. @return the image generation config */ + public ImageGenerationConfig getImageGeneration() { + return imageGeneration; + } + + /** + * Sets image generation config. @param imageGeneration the image generation + * config + */ + public void setImageGeneration(ImageGenerationConfig imageGeneration) { + this.imageGeneration = imageGeneration; + } + /** Sets tool-search config. @param toolSearch the tool-search config */ public void setToolSearch(ToolSearchConfig toolSearch) { this.toolSearch = toolSearch; @@ -956,6 +980,23 @@ public void clearEnableSkills() { this.enableSkills = null; } + /** Gets skill-provider availability flag. @return the flag */ + public Boolean getHasSkillProvider() { + return hasSkillProvider; + } + + /** Sets skill-provider availability flag. @param hasSkillProvider the flag */ + public void setHasSkillProvider(boolean hasSkillProvider) { + this.hasSkillProvider = hasSkillProvider; + } + + /** + * Clears the hasSkillProvider setting, reverting to the default behavior. + */ + public void clearHasSkillProvider() { + this.hasSkillProvider = null; + } + /** Gets embedding cache storage mode. @return the mode */ public String getEmbeddingCacheStorage() { return embeddingCacheStorage; @@ -1220,6 +1261,21 @@ public void clearEnableManagedSettings() { this.enableManagedSettings = null; } + /** Gets managed model lock enforcement. @return the flag, or {@code null} */ + public Boolean getEnforceManagedModelDefaults() { + return enforceManagedModelDefaults; + } + + /** Sets managed model lock enforcement. @param enforce whether to enforce */ + public void setEnforceManagedModelDefaults(boolean enforce) { + this.enforceManagedModelDefaults = enforce; + } + + /** Clears managed model lock enforcement. */ + public void clearEnforceManagedModelDefaults() { + this.enforceManagedModelDefaults = 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/ImageGenerationConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/ImageGenerationConfig.java new file mode 100644 index 0000000000..79b336b4da --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ImageGenerationConfig.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.github.copilot.CopilotExperimental; + +/** + * Experimental. This API may change or be removed in a future + * release. Opt in to image generation through an authorized Copilot image + * model. Omission disables on create/cold resume and preserves resident resume + * state. Not persisted: re-supply after runtime restart. Policy, permissions, + * offline mode, and tool filters still apply. BYOK image generation is not + * supported. + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +@CopilotExperimental +public class ImageGenerationConfig { + @JsonProperty("enabled") + private Boolean enabled; + + /** + * Gets the image generation opt-in. + * + * @return true to opt in, false to disable, or null when unset + */ + public Boolean getEnabled() { + return enabled; + } + + /** + * Enables or disables image generation. + * + * @param enabled + * the opt-in, or null when unset + * @return this config for method chaining + */ + public ImageGenerationConfig setEnabled(Boolean enabled) { + this.enabled = enabled; + return this; + } +} diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java b/java/sdk/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java index 6755d959b3..f79277c1a1 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ManagedSettingsPermissions.java @@ -16,6 +16,9 @@ public final class ManagedSettingsPermissions { @JsonProperty("disableBypassPermissionsMode") private String disableBypassPermissionsMode; + @JsonProperty("disableAssistedPermissionsMode") + private Boolean disableAssistedPermissionsMode; + @JsonProperty("deny") private List deny; @@ -25,6 +28,9 @@ public final class ManagedSettingsPermissions { @JsonProperty("allow") private List allow; + @JsonProperty("limitTo") + private List limitTo; + /** @return the bypass-permissions policy, or {@code null} when unset */ public String getDisableBypassPermissionsMode() { return disableBypassPermissionsMode; @@ -44,6 +50,25 @@ public ManagedSettingsPermissions setDisableBypassPermissionsMode(String value) return this; } + /** + * @return whether Assisted Permissions are disabled, or {@code null} when unset + */ + public Boolean getDisableAssistedPermissionsMode() { + return disableAssistedPermissionsMode; + } + + /** + * Prevents Assisted Permissions from being activated when true. + * + * @param value + * whether Assisted Permissions are disabled + * @return this policy + */ + public ManagedSettingsPermissions setDisableAssistedPermissionsMode(Boolean value) { + this.disableAssistedPermissionsMode = value; + return this; + } + /** @return rules that deny matching operations, or {@code null} when unset */ public List getDeny() { return deny; @@ -88,4 +113,25 @@ public ManagedSettingsPermissions setAllow(List rules) { this.allow = rules == null ? null : new ArrayList<>(rules); return this; } + + /** + * @return the closed-world host boundary, or {@code null} when unset + */ + public List getLimitTo() { + return limitTo; + } + + /** + * Every network request must match this list, and multiple managed layers + * intersect their lists. A present empty list denies all hosts. + * + * @param rules + * host-only {@code Domain(hostname)}, {@code Domain(IP)}, or + * {@code Domain(*.example.com)} boundary rules + * @return this policy + */ + public ManagedSettingsPermissions setLimitTo(List rules) { + this.limitTo = rules == null ? null : new ArrayList<>(rules); + return this; + } } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/NamedProviderConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/NamedProviderConfig.java index e3b090019b..5e2e69cde7 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/NamedProviderConfig.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/NamedProviderConfig.java @@ -51,6 +51,9 @@ public class NamedProviderConfig { @JsonProperty("wireApi") private String wireApi; + @JsonProperty("modelProvider") + private String modelProvider; + @JsonProperty("baseUrl") private String baseUrl; @@ -144,6 +147,30 @@ public NamedProviderConfig setWireApi(String wireApi) { return this; } + /** + * Gets the product serving this provider's models. + * + * @return the model provider, such as "ollama" or "lm_studio" + */ + public String getModelProvider() { + return modelProvider; + } + + /** + * Sets the product serving this provider's models, reported in telemetry as + * {@code model_provider}. Allowed values are "openai", "anthropic", + * "azure_openai", "ollama", "lm_studio", "foundry_local", and "llama_cpp"; only + * affects telemetry. + * + * @param modelProvider + * the model provider + * @return this config for method chaining + */ + public NamedProviderConfig setModelProvider(String modelProvider) { + this.modelProvider = modelProvider; + return this; + } + /** * Gets the base URL for the API. * diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ProviderConfig.java b/java/sdk/src/main/java/com/github/copilot/rpc/ProviderConfig.java index 3d6faba34d..db04746c38 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/ProviderConfig.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ProviderConfig.java @@ -47,6 +47,9 @@ public class ProviderConfig { @JsonProperty("transport") private String transport; + @JsonProperty("modelProvider") + private String modelProvider; + @JsonProperty("baseUrl") private String baseUrl; @@ -153,6 +156,30 @@ public ProviderConfig setTransport(String transport) { return this; } + /** + * Gets the product serving the model. + * + * @return the model provider, such as "ollama" or "lm_studio" + */ + public String getModelProvider() { + return modelProvider; + } + + /** + * Sets the product serving the model, reported in telemetry as + * {@code model_provider}. Allowed values are "openai", "anthropic", + * "azure_openai", "ollama", "lm_studio", "foundry_local", and "llama_cpp"; only + * affects telemetry. + * + * @param modelProvider + * the model provider + * @return this config for method chaining + */ + public ProviderConfig setModelProvider(String modelProvider) { + this.modelProvider = modelProvider; + return this; + } + /** * Gets the base URL for the API. * 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 4bc7de4803..44c965bab0 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 @@ -15,6 +15,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.github.copilot.CopilotExperimental; +import com.github.copilot.SkillProvider; import com.github.copilot.generated.SessionEvent; import com.github.copilot.generated.rpc.DiagnosticsConfiguration; import com.github.copilot.generated.rpc.SessionLimitsConfig; @@ -83,6 +84,7 @@ public class ResumeSessionConfig { private Boolean enableHostGitOperations; private Boolean enableSessionStore; private Boolean enableSkills; + private SkillProvider skillProvider; private String embeddingCacheStorage; private boolean disableResume; private boolean streaming; @@ -100,6 +102,7 @@ public class ResumeSessionConfig { private List pluginDirectories; private LargeToolOutputConfig largeOutput; private ToolSearchConfig toolSearch; + private ImageGenerationConfig imageGeneration; private MemoryConfiguration memory; private List disabledSkills; private List disabledMcpServers; @@ -117,6 +120,7 @@ public class ResumeSessionConfig { private String remoteSession; private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; + private Boolean enforceManagedModelDefaults; private Map featureFlags; private ManagedSettings managedSettings; @@ -1311,6 +1315,35 @@ public ResumeSessionConfig clearEnableSkills() { return this; } + /** + * Gets the session-scoped skill provider for the resumed session. + * + * @return the skill provider, or {@code null} when not configured + * @apiNote This API is experimental and may change in a future version. + */ + @CopilotExperimental + @JsonIgnore + public SkillProvider getSkillProvider() { + return skillProvider; + } + + /** + * Sets the session-scoped skill provider for the resumed session. + *

    + * Skill provider registrations are not persisted; re-supply the provider when + * resuming. + * + * @param skillProvider + * the skill provider to expose to the runtime + * @return this config for method chaining + * @apiNote This API is experimental and may change in a future version. + */ + @CopilotExperimental + public ResumeSessionConfig setSkillProvider(SkillProvider skillProvider) { + this.skillProvider = skillProvider; + return this; + } + /** * Gets the embedding cache storage mode. * @@ -1741,6 +1774,29 @@ public ToolSearchConfig getToolSearch() { return toolSearch; } + /** + * Gets the non-persisted image generation opt-in. + * + * @return the image generation config, or null when unset + */ + @CopilotExperimental + public ImageGenerationConfig getImageGeneration() { + return imageGeneration; + } + + /** + * Sets the image generation opt-in. Re-supply after runtime restart. + * + * @param imageGeneration + * the image generation config + * @return this config for method chaining + */ + @CopilotExperimental + public ResumeSessionConfig setImageGeneration(ImageGenerationConfig imageGeneration) { + this.imageGeneration = imageGeneration; + return this; + } + /** * Sets the tool-search configuration. * @@ -2171,6 +2227,28 @@ public ResumeSessionConfig setEnableManagedSettings(boolean enableManagedSetting return this; } + /** + * Gets whether locked managed model controls are enforced on resume. + * + * @return the explicit choice, or empty to use the runtime default + */ + @JsonIgnore + public Optional getEnforceManagedModelDefaults() { + return Optional.ofNullable(enforceManagedModelDefaults); + } + + /** + * Sets whether locked managed model controls are enforced after resume. + * + * @param enforceManagedModelDefaults + * whether to enforce locked managed model controls + * @return this config for method chaining + */ + public ResumeSessionConfig setEnforceManagedModelDefaults(boolean enforceManagedModelDefaults) { + this.enforceManagedModelDefaults = enforceManagedModelDefaults; + return this; + } + /** @return host-injected managed settings, or {@code null} when unset */ public ManagedSettings getManagedSettings() { return managedSettings; @@ -2244,6 +2322,7 @@ public ResumeSessionConfig clone() { copy.enableHostGitOperations = this.enableHostGitOperations; copy.enableSessionStore = this.enableSessionStore; copy.enableSkills = this.enableSkills; + copy.skillProvider = this.skillProvider; copy.embeddingCacheStorage = this.embeddingCacheStorage; copy.disableResume = this.disableResume; copy.streaming = this.streaming; @@ -2265,6 +2344,7 @@ public ResumeSessionConfig clone() { copy.pluginDirectories = this.pluginDirectories != null ? new ArrayList<>(this.pluginDirectories) : null; copy.largeOutput = this.largeOutput; copy.toolSearch = this.toolSearch; + copy.imageGeneration = this.imageGeneration; copy.memory = this.memory; copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null; copy.disabledMcpServers = this.disabledMcpServers != null ? new ArrayList<>(this.disabledMcpServers) : null; @@ -2283,6 +2363,7 @@ public ResumeSessionConfig clone() { copy.featureFlags = this.featureFlags != null ? new java.util.HashMap<>(this.featureFlags) : null; copy.expAssignments = this.expAssignments; copy.enableManagedSettings = this.enableManagedSettings; + copy.enforceManagedModelDefaults = this.enforceManagedModelDefaults; 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 d25893ff19..4a5e2a9a7c 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 @@ -147,6 +147,10 @@ public final class ResumeSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableSkills; + @JsonProperty("hasSkillProvider") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean hasSkillProvider; + @JsonProperty("embeddingCacheStorage") @JsonInclude(JsonInclude.Include.NON_NULL) private String embeddingCacheStorage; @@ -205,6 +209,9 @@ public final class ResumeSessionRequest { @JsonProperty("toolSearch") private ToolSearchConfig toolSearch; + @JsonProperty("imageGeneration") + private ImageGenerationConfig imageGeneration; + @JsonProperty("memory") private MemoryConfiguration memory; @@ -261,6 +268,10 @@ public final class ResumeSessionRequest { @JsonInclude(JsonInclude.Include.NON_NULL) private Boolean enableManagedSettings; + @JsonProperty("enforceManagedModelDefaults") + @JsonInclude(JsonInclude.Include.NON_NULL) + private Boolean enforceManagedModelDefaults; + @JsonProperty("managedSettings") @JsonInclude(JsonInclude.Include.NON_NULL) private ManagedSettings managedSettings; @@ -764,6 +775,23 @@ public void clearEnableSkills() { this.enableSkills = null; } + /** Gets skill-provider availability flag. @return the flag */ + public Boolean getHasSkillProvider() { + return hasSkillProvider; + } + + /** Sets skill-provider availability flag. @param hasSkillProvider the flag */ + public void setHasSkillProvider(boolean hasSkillProvider) { + this.hasSkillProvider = hasSkillProvider; + } + + /** + * Clears the hasSkillProvider setting, reverting to the default behavior. + */ + public void clearHasSkillProvider() { + this.hasSkillProvider = null; + } + /** Gets embedding cache storage mode. @return the mode */ public String getEmbeddingCacheStorage() { return embeddingCacheStorage; @@ -1005,6 +1033,19 @@ public ToolSearchConfig getToolSearch() { return toolSearch; } + /** Gets image generation config. @return the image generation config */ + public ImageGenerationConfig getImageGeneration() { + return imageGeneration; + } + + /** + * Sets image generation config. @param imageGeneration the image generation + * config + */ + public void setImageGeneration(ImageGenerationConfig imageGeneration) { + this.imageGeneration = imageGeneration; + } + /** Sets tool-search config. @param toolSearch the tool-search config */ public void setToolSearch(ToolSearchConfig toolSearch) { this.toolSearch = toolSearch; @@ -1250,6 +1291,21 @@ public void clearEnableManagedSettings() { this.enableManagedSettings = null; } + /** Gets managed model lock enforcement. @return the flag, or {@code null} */ + public Boolean getEnforceManagedModelDefaults() { + return enforceManagedModelDefaults; + } + + /** Sets managed model lock enforcement. @param enforce whether to enforce */ + public void setEnforceManagedModelDefaults(boolean enforce) { + this.enforceManagedModelDefaults = enforce; + } + + /** Clears managed model lock enforcement. */ + public void clearEnforceManagedModelDefaults() { + this.enforceManagedModelDefaults = 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 836b972ae2..e912527abc 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 @@ -15,6 +15,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.github.copilot.CopilotExperimental; +import com.github.copilot.SkillProvider; import com.github.copilot.generated.SessionEvent; import com.github.copilot.generated.rpc.DiagnosticsConfiguration; import com.github.copilot.generated.rpc.SessionLimitsConfig; @@ -88,6 +89,7 @@ public class SessionConfig { private List pluginDirectories; private LargeToolOutputConfig largeOutput; private ToolSearchConfig toolSearch; + private ImageGenerationConfig imageGeneration; private MemoryConfiguration memory; private List disabledSkills; private List disabledMcpServers; @@ -101,6 +103,7 @@ public class SessionConfig { private Boolean enableHostGitOperations; private Boolean enableSessionStore; private Boolean enableSkills; + private SkillProvider skillProvider; private String embeddingCacheStorage; private ModelCapabilitiesOverride modelCapabilities; private Consumer onEvent; @@ -117,6 +120,7 @@ public class SessionConfig { private CloudSessionOptions cloud; private CopilotExpAssignmentResponse expAssignments; private Boolean enableManagedSettings; + private Boolean enforceManagedModelDefaults; private Map featureFlags; private ManagedSettings managedSettings; @@ -1350,6 +1354,29 @@ public ToolSearchConfig getToolSearch() { return toolSearch; } + /** + * Gets the non-persisted image generation opt-in. + * + * @return the image generation config, or null when unset + */ + @CopilotExperimental + public ImageGenerationConfig getImageGeneration() { + return imageGeneration; + } + + /** + * Sets the image generation opt-in. Re-supply after runtime restart. + * + * @param imageGeneration + * the image generation config + * @return this config for method chaining + */ + @CopilotExperimental + public SessionConfig setImageGeneration(ImageGenerationConfig imageGeneration) { + this.imageGeneration = imageGeneration; + return this; + } + /** * Sets the tool-search override configuration. When {@code null}, the runtime * default tool-search behavior applies. @@ -1759,6 +1786,35 @@ public SessionConfig clearEnableSkills() { return this; } + /** + * Gets the session-scoped skill provider. + * + * @return the skill provider, or {@code null} when not configured + * @apiNote This API is experimental and may change in a future version. + */ + @CopilotExperimental + @JsonIgnore + public SkillProvider getSkillProvider() { + return skillProvider; + } + + /** + * Sets the session-scoped skill provider. + *

    + * Re-supply this provider when resuming a session. Skill providers are not + * supported for cloud sessions. + * + * @param skillProvider + * the skill provider to expose to the runtime + * @return this config instance for method chaining + * @apiNote This API is experimental and may change in a future version. + */ + @CopilotExperimental + public SessionConfig setSkillProvider(SkillProvider skillProvider) { + this.skillProvider = skillProvider; + return this; + } + /** * Gets the embedding cache storage mode. * @@ -2262,6 +2318,29 @@ public SessionConfig setEnableManagedSettings(boolean enableManagedSettings) { return this; } + /** + * Gets whether locked managed model controls are enforced. + * + * @return the explicit choice, or empty to use the runtime default + */ + @JsonIgnore + public Optional getEnforceManagedModelDefaults() { + return Optional.ofNullable(enforceManagedModelDefaults); + } + + /** + * Sets whether locked managed model controls are enforced for this session. + * Conflicting model changes are rejected; overridable defaults remain mutable. + * + * @param enforceManagedModelDefaults + * whether to enforce locked managed model controls + * @return this config instance for method chaining + */ + public SessionConfig setEnforceManagedModelDefaults(boolean enforceManagedModelDefaults) { + this.enforceManagedModelDefaults = enforceManagedModelDefaults; + return this; + } + /** * Gets host-injected managed settings for this session. * @@ -2353,6 +2432,7 @@ public SessionConfig clone() { copy.pluginDirectories = this.pluginDirectories != null ? new ArrayList<>(this.pluginDirectories) : null; copy.largeOutput = this.largeOutput; copy.toolSearch = this.toolSearch; + copy.imageGeneration = this.imageGeneration; copy.memory = this.memory; copy.disabledSkills = this.disabledSkills != null ? new ArrayList<>(this.disabledSkills) : null; copy.disabledMcpServers = this.disabledMcpServers != null ? new ArrayList<>(this.disabledMcpServers) : null; @@ -2366,6 +2446,7 @@ public SessionConfig clone() { copy.enableHostGitOperations = this.enableHostGitOperations; copy.enableSessionStore = this.enableSessionStore; copy.enableSkills = this.enableSkills; + copy.skillProvider = this.skillProvider; copy.embeddingCacheStorage = this.embeddingCacheStorage; copy.modelCapabilities = this.modelCapabilities; copy.onEvent = this.onEvent; @@ -2383,6 +2464,7 @@ public SessionConfig clone() { copy.featureFlags = this.featureFlags != null ? new java.util.HashMap<>(this.featureFlags) : null; copy.expAssignments = this.expAssignments; copy.enableManagedSettings = this.enableManagedSettings; + copy.enforceManagedModelDefaults = this.enforceManagedModelDefaults; copy.managedSettings = this.managedSettings; return copy; } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SessionHooks.java b/java/sdk/src/main/java/com/github/copilot/rpc/SessionHooks.java index e476f888ed..30febc54ce 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/SessionHooks.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SessionHooks.java @@ -46,6 +46,8 @@ public class SessionHooks { private SessionStartHandler onSessionStart; private SessionEndHandler onSessionEnd; private AgentStopHandler onAgentStop; + private SubagentStartHandler onSubagentStart; + private SubagentStopHandler onSubagentStop; /** * Gets the pre-tool-use handler. @@ -254,6 +256,48 @@ public SessionHooks setOnAgentStop(AgentStopHandler onAgentStop) { return this; } + /** + * Gets the handler called before a subagent's first turn. + * + * @return the handler, or {@code null} if not set + */ + public SubagentStartHandler getOnSubagentStart() { + return onSubagentStart; + } + + /** + * Sets the handler called before a subagent's first turn. + * + * @param onSubagentStart + * the handler + * @return this instance for method chaining + */ + public SessionHooks setOnSubagentStart(SubagentStartHandler onSubagentStart) { + this.onSubagentStart = onSubagentStart; + return this; + } + + /** + * Gets the handler called after a subagent's turn. + * + * @return the handler, or {@code null} if not set + */ + public SubagentStopHandler getOnSubagentStop() { + return onSubagentStop; + } + + /** + * Sets the handler called after a subagent's turn. + * + * @param onSubagentStop + * the handler + * @return this instance for method chaining + */ + public SessionHooks setOnSubagentStop(SubagentStopHandler onSubagentStop) { + this.onSubagentStop = onSubagentStop; + return this; + } + /** * Returns whether any hooks are registered. * @@ -262,6 +306,6 @@ public SessionHooks setOnAgentStop(AgentStopHandler onAgentStop) { public boolean hasHooks() { return onPreToolUse != null || onPreMcpToolCall != null || onPostToolUse != null || onPostToolUseFailure != null || onUserPromptSubmitted != null || onUserPromptTransformed != null || onSessionStart != null - || onSessionEnd != null || onAgentStop != null; + || onSessionEnd != null || onAgentStop != null || onSubagentStart != null || onSubagentStop != null; } } diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHandler.java new file mode 100644 index 0000000000..273da17aac --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHandler.java @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import java.util.concurrent.CompletableFuture; + +/** + * Handler invoked before a subagent's first turn. + */ +@FunctionalInterface +public interface SubagentStartHandler { + + /** + * Handles a subagent-start hook invocation. + * + * @param input + * the hook input + * @param invocation + * context about the invocation + * @return context to prepend to the subagent's prompt, or {@code null} + */ + CompletableFuture handle(SubagentStartHookInput input, HookInvocation invocation); +} 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 new file mode 100644 index 0000000000..0ab4d426b4 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHookInput.java @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Input received before a subagent's first turn. + * + * @param sessionId + * the parent session ID + * @param timestamp + * Unix timestamp in milliseconds + * @param cwd + * the parent session's working directory + * @param transcriptPath + * the parent session transcript path + * @param agentName + * the subagent definition name + * @param agentDisplayName + * the optional subagent display name + * @param agentDescription + * the optional subagent description + */ +@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) { +} diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHookOutput.java new file mode 100644 index 0000000000..5a19ad594f --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStartHookOutput.java @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Output for a subagent-start hook. + * + * @param additionalContext + * context prepended to the subagent's initial prompt, or + * {@code null} + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public record SubagentStartHookOutput(@JsonProperty("additionalContext") String additionalContext) { +} diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHandler.java b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHandler.java new file mode 100644 index 0000000000..c4c00fd622 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHandler.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import java.util.concurrent.CompletableFuture; + +/** + * Handler invoked after a subagent's turn. + */ +@FunctionalInterface +public interface SubagentStopHandler { + + /** + * Handles a subagent-stop hook invocation. + * + * @param input + * the hook input + * @param invocation + * context about the invocation + * @return a block decision or replacement response, or {@code null} to allow + * the stop + */ + CompletableFuture handle(SubagentStopHookInput input, HookInvocation invocation); +} 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 new file mode 100644 index 0000000000..299238e034 --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHookInput.java @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Input received after a subagent's turn. + * + * @param sessionId + * the parent session ID + * @param timestamp + * Unix timestamp in milliseconds + * @param cwd + * the parent session's working directory + * @param transcriptPath + * the parent session transcript path + * @param agentName + * the subagent definition name + * @param agentType + * the subagent type + * @param agentId + * the subagent ID, if available + * @param agentDisplayName + * the optional subagent display name + * @param agentDescription + * the optional subagent description + * @param stopReason + * the reason the subagent stopped + * @param response + * the subagent's last assistant response + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public record SubagentStopHookInput(@JsonProperty("sessionId") String sessionId, + @JsonProperty("timestamp") long timestamp, @JsonProperty("cwd") String cwd, + @JsonProperty("transcriptPath") String transcriptPath, @JsonProperty("agentName") String agentName, + @JsonProperty("agentType") String agentType, @JsonProperty("agentId") String agentId, + @JsonProperty("agentDisplayName") String agentDisplayName, + @JsonProperty("agentDescription") String agentDescription, @JsonProperty("stopReason") String stopReason, + @JsonProperty("response") String response) { +} diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHookOutput.java b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHookOutput.java new file mode 100644 index 0000000000..364d7ea88e --- /dev/null +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SubagentStopHookOutput.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Output for a subagent-stop hook. + * + * @param decision + * {@code "block"} to run another subagent turn, or {@code "allow"} + * (or {@code null}) to allow the stop; other values fail the + * subagent + * @param reason + * the nonempty follow-up instruction required when blocking; invalid + * without {@code decision = "block"} + * @param modifiedResponse + * replacement final response when the stop is allowed + */ +@JsonInclude(JsonInclude.Include.NON_NULL) +public record SubagentStopHookOutput(@JsonProperty("decision") String decision, @JsonProperty("reason") String reason, + @JsonProperty("modifiedResponse") String modifiedResponse) { +} diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/SystemMessageSections.java b/java/sdk/src/main/java/com/github/copilot/rpc/SystemMessageSections.java index ca410e497e..d575622f00 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/SystemMessageSections.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/SystemMessageSections.java @@ -61,9 +61,9 @@ public abstract sealed class SystemMessageSections permits SystemPromptSections public static final String CUSTOM_INSTRUCTIONS = "custom_instructions"; /** - * Runtime-provided context and instructions (e.g. system notifications, - * memories, workspace context, mode-specific instructions, content-exclusion - * policy). + * Runtime-provided system-prompt context and instructions, such as system + * notifications, memories, workspace context, and content-exclusion policy. + * Mode-specific instructions can travel in transition messages instead. * * @since 1.3.0 */ @@ -71,7 +71,8 @@ public abstract sealed class SystemMessageSections permits SystemPromptSections /** * End-of-prompt instructions: parallel tool calling, persistence, task - * completion. + * completion, and configured subagent-model guidance when the task tool is + * available. */ public static final String LAST_INSTRUCTIONS = "last_instructions"; diff --git a/java/sdk/src/main/java/com/github/copilot/rpc/ToolInvocation.java b/java/sdk/src/main/java/com/github/copilot/rpc/ToolInvocation.java index efe24fd6ae..a0a004176a 100644 --- a/java/sdk/src/main/java/com/github/copilot/rpc/ToolInvocation.java +++ b/java/sdk/src/main/java/com/github/copilot/rpc/ToolInvocation.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.CopilotExperimental; import com.github.copilot.generated.rpc.CurrentToolMetadata; /** @@ -43,6 +44,8 @@ public final class ToolInvocation { private String toolName; private JsonNode argumentsNode; private List availableTools; + private String agentId; + private String requestId; /** * Gets the session ID where the tool was invoked. @@ -208,4 +211,71 @@ public ToolInvocation setAvailableTools(List availableTools this.availableTools = availableTools; return this; } + + /** + * Gets the sub-agent instance that issued this call. + *

    + * Experimental. This is the {@code agentId} of the sub-agent's + * session events. It is {@code null} for a root-agent call, for every call when + * the runtime does not attribute sub-agent tool calls, and for a call that did + * not arrive as an external tool request. + * + * @return the sub-agent instance id, or {@code null} when no sub-agent is named + * @since 1.0.15-preview.1 + */ + @CopilotExperimental + public String getAgentId() { + return agentId; + } + + /** + * Sets the sub-agent instance that issued this call. + *

    + * Note: This method is intended for internal SDK use. Users + * typically do not need to call this method directly. + * + * @param agentId + * the sub-agent instance id, or {@code null} when no sub-agent is + * named + * @return this invocation for method chaining + */ + @CopilotExperimental + public ToolInvocation setAgentId(String agentId) { + this.agentId = agentId; + return this; + } + + /** + * Gets the runtime request id of this call. + *

    + * Experimental. This is the key + * {@code session.tools.handlePendingToolCall} and + * {@code session.tools.listPendingRequests} use. A root-agent call stays + * answerable after the session is resumed with pending work continued; a + * sub-agent's call can be answered only while that sub-agent is still running. + * + * @return the request id, or {@code null} if the call did not arrive as an + * external tool request + * @since 1.0.15-preview.1 + */ + @CopilotExperimental + public String getRequestId() { + return requestId; + } + + /** + * Sets the runtime request id of this call. + *

    + * Note: This method is intended for internal SDK use. Users + * typically do not need to call this method directly. + * + * @param requestId + * the request id + * @return this invocation for method chaining + */ + @CopilotExperimental + public ToolInvocation setRequestId(String requestId) { + this.requestId = requestId; + return this; + } } diff --git a/java/sdk/src/test/java/com/github/copilot/AhpHostTest.java b/java/sdk/src/test/java/com/github/copilot/AhpHostTest.java index 44e3caa562..136248cb0a 100644 --- a/java/sdk/src/test/java/com/github/copilot/AhpHostTest.java +++ b/java/sdk/src/test/java/com/github/copilot/AhpHostTest.java @@ -7,11 +7,8 @@ import static org.junit.jupiter.api.Assertions.*; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.copilot.generated.rpc.HostCreateSessionParams; import com.github.copilot.generated.rpc.HostGitHubEnvironmentOptions; import com.github.copilot.generated.rpc.HostLocalServerOptions; -import com.github.copilot.generated.rpc.HostSessionCreateCallback; import com.github.copilot.generated.rpc.ServerHostApi; import com.github.copilot.generated.rpc.SessionLimitsConfig; import com.github.copilot.rpc.CopilotClientOptions; @@ -75,7 +72,7 @@ void forwardsEnvironmentFieldsForRuntimeValidation() throws Exception { @CsvSource({"true,false", "false,true", "true,true"}) void forwardsExplicitTransportsAndOptionalReadiness(boolean local, boolean github) throws Exception { try (var server = new FakeRuntime(); var client = server.client()) { - var options = new AhpHostOptions(); + var options = new AhpHostOptions().setComputeId("compute"); if (local) { options.setLocalServer(new HostLocalServerOptions("127.0.0.1", 0L, "test-token", true)); } @@ -84,6 +81,7 @@ void forwardsExplicitTransportsAndOptionalReadiness(boolean local, boolean githu } try (var host = await(client.startAhpHost(options))) { var request = await(server.startRequest); + assertEquals("compute", request.path("computeId").asText()); assertEquals(local, request.hasNonNull("localServer")); assertEquals(github, request.hasNonNull("githubEnvironment")); for (String oldField : List.of("hostname", "port", "token", "requireConnectionToken")) { @@ -109,50 +107,54 @@ void forwardsExplicitTransportsAndOptionalReadiness(boolean local, boolean githu void snapshotsOptionsBeforeConnecting() throws Exception { try (var server = new FakeRuntime(); var client = server.client()) { server.finishConnect = new CompletableFuture<>(); - var options = localOptions().setGithubEnvironment(new HostGitHubEnvironmentOptions("original", "compute")); + var options = localOptions().setComputeId("compute") + .setGithubEnvironment(new HostGitHubEnvironmentOptions("original", "compute")); var pending = client.startAhpHost(options); await(server.connectEntered); try { - options.setLocalServer(null).setGithubEnvironment(new HostGitHubEnvironmentOptions("", "")); + options.setComputeId("changed").setLocalServer(null) + .setGithubEnvironment(new HostGitHubEnvironmentOptions("", "")); } finally { server.finishConnect.complete(null); } try (var host = await(pending)) { var request = await(server.startRequest); assertTrue(request.hasNonNull("localServer")); + assertEquals("compute", request.path("computeId").asText()); assertEquals("original", request.path("githubEnvironment").path("name").asText()); assertEquals("compute", request.path("githubEnvironment").path("computeId").asText()); } } } + @Test + void legacyEnvironmentOptionsOmitConnectionBinding() throws Exception { + try (var server = new FakeRuntime(); + var client = server.client(); + var host = await(client.startAhpHost(new AhpHostOptions() + .setGithubEnvironment(new HostGitHubEnvironmentOptions("test host", "compute"))))) { + assertFalse(await(server.startRequest).path("githubEnvironment").has("requireConnectionBinding")); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void forwardsExplicitConnectionBinding(boolean required) throws Exception { + try (var server = new FakeRuntime(); + var client = server.client(); + var host = await(client.startAhpHost(new AhpHostOptions() + .setGithubEnvironment(new HostGitHubEnvironmentOptions("test host", "compute", required))))) { + assertEquals(required, + await(server.startRequest).path("githubEnvironment").path("requireConnectionBinding").asBoolean()); + } + } + @Test void publicHostApiExposesOnlyOwnerOperations() { var methods = Arrays.stream(ServerHostApi.class.getMethods()) .filter(method -> method.getDeclaringClass() == ServerHostApi.class).map(method -> method.getName()) .collect(Collectors.toSet()); - assertEquals(Set.of("start", "dispose", "publishSession"), methods); - } - - @Test - void generatedHandoffMapsRoundTripScalarAndStructuredSettings() throws Exception { - var mapper = new ObjectMapper(); - Map config = new HashMap<>(); - config.put("sessionId", "requested"); - config.put("workingDirectory", "/workspace"); - config.put("streaming", true); - config.put("threshold", 42); - config.put("additionalDirectories", List.of("/other")); - config.put("featureFlags", Map.of("flag", true)); - config.put("optional", null); - var request = new HostCreateSessionParams("handoff", false, config); - assertEquals(config, - mapper.readValue(mapper.writeValueAsBytes(request), HostCreateSessionParams.class).config()); - var callback = mapper.readValue( - mapper.writeValueAsBytes( - Map.of("hostId", "host", "handoffId", "handoff", "resume", false, "config", config)), - HostSessionCreateCallback.class); - assertEquals(config, callback.config()); + assertEquals(Set.of("start", "dispose", "publishSession", "listSessions"), methods); } @Test @@ -354,6 +356,8 @@ void publishesDisposesRepeatedlyAndRetainsOriginalTransport() throws Exception { }))); assertNull(host.getPid()); assertEquals("ahp-session:/resident", await(host.publishSession("resident")).sessionUri()); + assertTrue(await(host.listSessions()).sessions().isEmpty()); + assertEquals(host.getHostId(), await(server.listSessionsRequest).path("hostId").asText()); await(CompletableFuture.allOf(host.dispose(), host.dispose())); await(exited); assertEquals(2, server.disposals.get()); @@ -371,6 +375,7 @@ private static final class FakeRuntime implements AutoCloseable { final AtomicInteger disposals = new AtomicInteger(); final CompletableFuture startEntered = new CompletableFuture<>(); final CompletableFuture startRequest = new CompletableFuture<>(); + final CompletableFuture listSessionsRequest = new CompletableFuture<>(); final CompletableFuture disposedHost = new CompletableFuture<>(); final CompletableFuture connectEntered = new CompletableFuture<>(); volatile CompletableFuture finishConnect = CompletableFuture.completedFuture(null); @@ -414,6 +419,10 @@ private static final class FakeRuntime implements AutoCloseable { (id, params) -> respond(transport, id, Map.of("sessionId", params.get("sessionId").asText(), "sessionUri", "ahp-session:/" + params.get("sessionId").asText()))); + transport.registerMethodHandler("host.listSessions", (id, params) -> { + listSessionsRequest.complete(params); + respond(transport, id, Map.of("sessions", List.of())); + }); transport.registerMethodHandler("host.dispose", (id, params) -> { disposals.incrementAndGet(); try { diff --git a/java/sdk/src/test/java/com/github/copilot/BuiltinPluginDirectoriesTest.java b/java/sdk/src/test/java/com/github/copilot/BuiltinPluginDirectoriesTest.java index b5f89e5f9f..7a42c2c45e 100644 --- a/java/sdk/src/test/java/com/github/copilot/BuiltinPluginDirectoriesTest.java +++ b/java/sdk/src/test/java/com/github/copilot/BuiltinPluginDirectoriesTest.java @@ -90,15 +90,16 @@ JsonNode awaitBuiltinParams() throws Exception { private void acceptLoop() { try { Socket socket = serverSocket.accept(); - JsonRpcClient server = JsonRpcClient.fromSocket(socket); - server.registerMethodHandler("ping", (id, params) -> respond(server, id, - Map.of("message", "pong", "timestamp", "2026-09-21T00:00:00Z", "protocolVersion", 3))); - server.registerMethodHandler("connect", (id, params) -> respond(server, id, - Map.of("ok", true, "protocolVersion", 3, "version", "test"))); - server.registerMethodHandler("plugins.builtin.set", (id, params) -> { - builtinSetCount.incrementAndGet(); - builtinParams.complete(params); - respond(server, id, Map.of()); + JsonRpcClient server = JsonRpcClient.fromSocket(socket, rpc -> { + rpc.registerMethodHandler("ping", (id, params) -> respond(rpc, id, + Map.of("message", "pong", "timestamp", "2026-09-21T00:00:00Z", "protocolVersion", 3))); + rpc.registerMethodHandler("connect", (id, params) -> respond(rpc, id, + Map.of("ok", true, "protocolVersion", 3, "version", "test"))); + rpc.registerMethodHandler("plugins.builtin.set", (id, params) -> { + builtinSetCount.incrementAndGet(); + builtinParams.complete(params); + respond(rpc, id, Map.of()); + }); }); ready.complete(server); } catch (IOException e) { diff --git a/java/sdk/src/test/java/com/github/copilot/CapiSessionOptionsTest.java b/java/sdk/src/test/java/com/github/copilot/CapiSessionOptionsTest.java index aeb4ffe0ad..cc4e2fde3f 100644 --- a/java/sdk/src/test/java/com/github/copilot/CapiSessionOptionsTest.java +++ b/java/sdk/src/test/java/com/github/copilot/CapiSessionOptionsTest.java @@ -14,7 +14,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import java.util.stream.Stream; import com.fasterxml.jackson.databind.JsonNode; @@ -68,7 +70,7 @@ void omitsUnsetEnableWebSocketResponses() { } @ParameterizedTest - @CsvSource({"EFFICIENCY,efficiency", "BALANCE,balance", "INTELLIGENCE,intelligence", "FAST,fast"}) + @MethodSource("canonicalAutoTiers") void autoTierCanonicalValuesRoundTripAndForward(AutoTier tier, String value) throws Exception { var mapper = JsonRpcClient.getObjectMapper(); var capi = new CapiSessionOptions().setAutoTier(tier); @@ -90,13 +92,25 @@ void autoTierCanonicalValuesRoundTripAndForward(AutoTier tier, String value) thr } @Test - void autoTierRejectsNoncanonicalValues() { + void autoTierPreservesServerIdentifiersAndRejectsMalformedValues() throws Exception { + var mapper = JsonRpcClient.getObjectMapper(); for (String value : new String[]{"balanced", "Balance", "unknown"}) { + var tier = AutoTier.fromValue(value); + assertEquals(value, tier.getValue()); + assertEquals(value, mapper.valueToTree(tier).asText()); + assertEquals(tier, mapper.readValue("\"" + value + "\"", AutoTier.class)); + } + for (String value : new String[]{"", "two words", "\n"}) { assertThrows(IllegalArgumentException.class, () -> AutoTier.fromValue(value)); } assertNull(AutoTier.fromValue(null)); } + private static Stream canonicalAutoTiers() { + return Stream.of(Arguments.of(AutoTier.EFFICIENCY, "efficiency"), Arguments.of(AutoTier.BALANCE, "balance"), + Arguments.of(AutoTier.INTELLIGENCE, "intelligence"), Arguments.of(AutoTier.FAST, "fast")); + } + @Test void clearingAutoTierOmitsIt() { var capi = new CapiSessionOptions().setAutoTier(AutoTier.BALANCE).setAutoTier(null); diff --git a/java/sdk/src/test/java/com/github/copilot/ConfigCloneTest.java b/java/sdk/src/test/java/com/github/copilot/ConfigCloneTest.java index 8f937fcb99..fba9a6413b 100644 --- a/java/sdk/src/test/java/com/github/copilot/ConfigCloneTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ConfigCloneTest.java @@ -323,6 +323,17 @@ void sessionConfigGranularMultitenancyFieldsCopied() { assertFalse(cloned.getEnableSkills().orElse(true)); } + @Test + @AllowCopilotExperimental + void sessionConfigSkillProviderCloned() { + SkillProvider provider = testSkillProvider(); + SessionConfig original = new SessionConfig().setSkillProvider(provider); + + SessionConfig cloned = original.clone(); + + assertSame(provider, cloned.getSkillProvider()); + } + @Test void resumeSessionConfigEnableSessionTelemetryCopied() { ResumeSessionConfig original = new ResumeSessionConfig(); @@ -362,6 +373,17 @@ void resumeSessionConfigGranularMultitenancyFieldsCopied() { assertTrue(cloned.getEnableSkills().orElse(false)); } + @Test + @AllowCopilotExperimental + void resumeSessionConfigSkillProviderCloned() { + SkillProvider provider = testSkillProvider(); + ResumeSessionConfig original = new ResumeSessionConfig().setSkillProvider(provider); + + ResumeSessionConfig cloned = original.clone(); + + assertSame(provider, cloned.getSkillProvider()); + } + @Test void clonePreservesNullFields() { CopilotClientOptions opts = new CopilotClientOptions(); @@ -548,4 +570,19 @@ void resumeSessionConfigCloneCopiesModeSwitchHandlers() { assertSame(original.getOnExitPlanMode(), cloned.getOnExitPlanMode()); assertSame(original.getOnAutoModeSwitch(), cloned.getOnAutoModeSwitch()); } + + @AllowCopilotExperimental + private static SkillProvider testSkillProvider() { + return new SkillProvider() { + @Override + public CompletableFuture> listSkills() { + return CompletableFuture.completedFuture(List.of()); + } + + @Override + public CompletableFuture readSkill(String name) { + return CompletableFuture.completedFuture(null); + } + }; + } } diff --git a/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java b/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java index 9055cb19f7..7bc27fe321 100644 --- a/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java +++ b/java/sdk/src/test/java/com/github/copilot/CopilotClientTest.java @@ -9,11 +9,17 @@ import org.junit.jupiter.api.io.TempDir; import com.github.copilot.generated.ExternalToolRequestedEvent; +import com.github.copilot.rpc.CloudSessionOptions; import com.github.copilot.rpc.CopilotClientOptions; +import com.github.copilot.rpc.CopilotClientMode; +import com.github.copilot.rpc.CreateSessionRequest; +import com.github.copilot.rpc.CreateSessionResponse; import com.github.copilot.rpc.DeleteSessionResponse; import com.github.copilot.rpc.GitHubTokenProviderResult; import com.github.copilot.rpc.PermissionHandler; import com.github.copilot.rpc.PingResponse; +import com.github.copilot.rpc.ResumeSessionConfig; +import com.github.copilot.rpc.ResumeSessionResponse; import com.github.copilot.rpc.SessionConfig; import com.github.copilot.rpc.SessionLifecycleEvent; import com.github.copilot.rpc.SessionLifecycleEventTypes; @@ -22,6 +28,7 @@ import java.io.OutputStream; import java.lang.reflect.Field; import java.io.BufferedReader; +import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; @@ -176,6 +183,137 @@ void testDeleteSessionReleasesGitHubTokenProvider() throws Exception { } } + @Test + @AllowCopilotExperimental + void cloudSessionWithSkillProviderIsRejectedBeforeConnectingOrCallingProvider() throws Exception { + var providerCalls = new AtomicInteger(); + SkillProvider provider = new SkillProvider() { + @Override + public CompletableFuture> listSkills() { + providerCalls.incrementAndGet(); + return CompletableFuture.completedFuture(List.of()); + } + + @Override + public CompletableFuture readSkill(String name) { + providerCalls.incrementAndGet(); + return CompletableFuture.completedFuture(null); + } + }; + + try (var client = new CopilotClient(new CopilotClientOptions().setAutoStart(false))) { + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setCloud(new CloudSessionOptions()).setSkillProvider(provider); + + ExecutionException error = assertThrows(ExecutionException.class, () -> client.createSession(config).get()); + assertInstanceOf(IllegalArgumentException.class, error.getCause()); + assertEquals("Skill providers are not supported for cloud sessions.", error.getCause().getMessage()); + assertEquals(0, providerCalls.get()); + } + } + + @Test + @AllowCopilotExperimental + void emptyModeCreateWithSkillProviderSendsFlagsAndRegistersBeforeRpc() throws Exception { + var client = new CopilotClient(new CopilotClientOptions().setAutoStart(false).setMode(CopilotClientMode.EMPTY) + .setCopilotHome(System.getProperty("java.io.tmpdir"))); + var rpc = mock(JsonRpcClient.class); + setConnectionFuture(client, rpc, null); + SkillProvider provider = testSkillProvider(); + var createRequest = new AtomicReference(); + + when(rpc.invoke(eq("session.create"), any(), eq(CreateSessionResponse.class))).thenAnswer(invocation -> { + CreateSessionRequest request = invocation.getArgument(1); + createRequest.set(request); + assertTrue(request.getHasSkillProvider()); + assertFalse(request.getEnableSkills()); + assertSame(provider, registeredSkillProvider(client, "skill-provider-session")); + return CompletableFuture + .completedFuture(new CreateSessionResponse("skill-provider-session", null, null, null)); + }); + when(rpc.invoke(eq("session.options.update"), any(), eq(Void.class))) + .thenReturn(CompletableFuture.completedFuture(null)); + + try { + var config = new SessionConfig().setSessionId("skill-provider-session") + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setAvailableTools(List.of()) + .setSkillProvider(provider); + + CopilotSession session = client.createSession(config).get(); + + assertSame(provider, session.getSkillProvider()); + assertNotNull(createRequest.get()); + assertTrue(createRequest.get().getHasSkillProvider()); + assertFalse(createRequest.get().getEnableSkills()); + } finally { + client.forceStop().get(); + } + } + + @Test + @AllowCopilotExperimental + void emptyModeResumeValidationFailureKeepsResidentSkillProvider() throws Exception { + var client = new CopilotClient(new CopilotClientOptions().setAutoStart(false).setMode(CopilotClientMode.EMPTY) + .setCopilotHome(System.getProperty("java.io.tmpdir"))); + var rpc = mock(JsonRpcClient.class); + setConnectionFuture(client, rpc, null); + SkillProvider resident = testSkillProvider(); + when(rpc.invoke(eq("session.create"), any(), eq(CreateSessionResponse.class))).thenReturn( + CompletableFuture.completedFuture(new CreateSessionResponse("resident-session", null, null, null))); + when(rpc.invoke(eq("session.options.update"), any(), eq(Void.class))) + .thenReturn(CompletableFuture.completedFuture(null)); + + try { + client.createSession(new SessionConfig().setSessionId("resident-session") + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setAvailableTools(List.of()) + .setSkillProvider(resident)).get(); + + var resumeConfig = new ResumeSessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setSkillProvider(testSkillProvider()); + var error = assertThrows(ExecutionException.class, + () -> client.resumeSession("resident-session", resumeConfig).get()); + + assertInstanceOf(IllegalArgumentException.class, error.getCause()); + assertSame(resident, registeredSkillProvider(client, "resident-session")); + verify(rpc, never()).invoke(eq("session.resume"), any(), any()); + } finally { + client.forceStop().get(); + } + } + + @Test + @AllowCopilotExperimental + void failedResumeDoesNotEvictAConcurrentResume() throws Exception { + var client = new CopilotClient( + new CopilotClientOptions().setAutoStart(false).setCopilotHome(System.getProperty("java.io.tmpdir"))); + var rpc = mock(JsonRpcClient.class); + setConnectionFuture(client, rpc, null); + when(rpc.invoke(eq("session.create"), any(), eq(CreateSessionResponse.class))).thenReturn( + CompletableFuture.completedFuture(new CreateSessionResponse("shared-session", null, null, null))); + when(rpc.invoke(eq("session.options.update"), any(), eq(Void.class))) + .thenReturn(CompletableFuture.completedFuture(null)); + var firstResume = new CompletableFuture(); + when(rpc.invoke(eq("session.resume"), any(), eq(ResumeSessionResponse.class))).thenReturn(firstResume, + CompletableFuture.completedFuture(new ResumeSessionResponse("shared-session", null, null, null))); + + try { + client.createSession(new SessionConfig().setSessionId("shared-session") + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(testSkillProvider())).get(); + var failing = client.resumeSession("shared-session", new ResumeSessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(testSkillProvider())); + SkillProvider winner = testSkillProvider(); + client.resumeSession("shared-session", new ResumeSessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(winner)).get(); + + firstResume.completeExceptionally(new RuntimeException("resume failed")); + + assertThrows(ExecutionException.class, failing::get); + assertSame(winner, registeredSkillProvider(client, "shared-session")); + } finally { + client.forceStop().get(); + } + } + @Test void testClientConstruction() { var client = new CopilotClient(); @@ -274,26 +412,64 @@ void testStartupWriteFailureDrainsStderrBeforeKillingChild(@TempDir Path tempDir process.exit(1); } }); - fs.closeSync(0); process.stdout.write('started\\n'); """); var options = new CopilotClientOptions().setCliPath(script.toString()).setUseStdio(true); var manager = spy(new CliServerManager(options)); var child = new AtomicReference(); + var failedOutput = new AtomicReference(); + var writeFailure = new IOException("Controlled startup write failure"); + doAnswer(invocation -> { + CliServerManager.ProcessInfo info = (CliServerManager.ProcessInfo) invocation.callRealMethod(); + child.set(info.process()); + // Closing fd 0 does not reliably fail the first parent write on Windows. + var output = new FilterOutputStream(info.process().getOutputStream()) { + @Override + public void write(int value) throws IOException { + throw writeFailure; + } + + @Override + public void write(byte[] bytes, int offset, int length) throws IOException { + throw writeFailure; + } + + @Override + public void close() throws IOException { + // No write succeeds, so there is nothing to flush on this failed transport. + out.close(); + } + }; + failedOutput.set(output); + var transportProcess = mock(Process.class, org.mockito.AdditionalAnswers.delegatesTo(info.process())); + doReturn(output).when(transportProcess).getOutputStream(); + return new CliServerManager.ProcessInfo(transportProcess, info.port()); + }).when(manager).startCliServer(); doAnswer(invocation -> { Process process = invocation.getArgument(0); - child.set(process); var reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8)); assertEquals("started", reader.readLine()); return invocation.callRealMethod(); }).when(manager).connectToServer(any(Process.class), isNull(), isNull()); doAnswer(invocation -> { // Release stderr only when startup drains it, while the child is still alive. + assertTrue(child.get().isAlive(), "Startup must drain stderr before killing the controlled child."); Files.createFile(tempDir.resolve("release-stderr")); return invocation.callRealMethod(); }).when(manager).awaitStderrReader(); - try (var client = new CopilotClient(options)) { + try (AutoCloseable childCleanup = () -> { + var process = child.get(); + if (process != null && process.isAlive()) { + process.destroyForcibly(); + assertTrue(process.waitFor(10, TimeUnit.SECONDS), "Could not reap the controlled child."); + } + }; var client = new CopilotClient(options); AutoCloseable outputCleanup = () -> { + var output = failedOutput.get(); + if (output != null) { + output.close(); + } + }) { Field managerField = CopilotClient.class.getDeclaredField("serverManager"); managerField.setAccessible(true); managerField.set(client, manager); @@ -307,14 +483,9 @@ void testStartupWriteFailureDrainsStderrBeforeKillingChild(@TempDir Path tempDir while (original.getCause() != null) { original = original.getCause(); } - assertInstanceOf(IOException.class, original); + assertSame(writeFailure, original, "Startup must preserve the actual failed transport write."); assertNotSame(cause, original, "The startup error must retain its original transport failure."); assertFalse(child.get().isAlive(), "Failed startup retained its child process."); - } finally { - if (child.get() != null && child.get().isAlive()) { - child.get().destroyForcibly(); - assertTrue(child.get().waitFor(10, TimeUnit.SECONDS), "Could not reap the controlled child."); - } } } @@ -579,6 +750,38 @@ void testOnLifecycleMultipleHandlers() throws Exception { // ===== getState() coverage ===== + @Test + void testGetStateDoesNotReportConnectedWhenStartFailsDuringStateRead() throws Exception { + var failure = new IOException("Startup failed"); + // Complete after the first status observation, whichever status getState reads + // first. + var future = new CompletableFuture() { + @Override + public boolean isDone() { + boolean done = super.isDone(); + completeExceptionally(failure); + return done; + } + + @Override + public boolean isCompletedExceptionally() { + boolean failed = super.isCompletedExceptionally(); + completeExceptionally(failure); + return failed; + } + }; + + try (var client = new CopilotClient(new CopilotClientOptions().setAutoStart(false))) { + setConnectionFuture(client, future); + + ConnectionState state = client.getState(); + assertTrue(state == ConnectionState.CONNECTING || state == ConnectionState.ERROR, + "Failed startup must not report CONNECTED, was: " + state); + assertTrue(future.isCompletedExceptionally()); + assertEquals(ConnectionState.ERROR, client.getState()); + } + } + @Test void testGetStateErrorAfterFailedStart() throws Exception { // Use a non-existent CLI path to trigger a startup failure @@ -793,8 +996,37 @@ private static void setConnectionFuture(CopilotClient client, JsonRpcClient rpc, constructor.setAccessible(true); var connection = constructor.newInstance(rpc, process, null, null); + setConnectionFuture(client, CompletableFuture.completedFuture(connection)); + } + + private static void setConnectionFuture(CopilotClient client, CompletableFuture future) throws Exception { Field field = CopilotClient.class.getDeclaredField("connectionFuture"); field.setAccessible(true); - field.set(client, CompletableFuture.completedFuture(connection)); + field.set(client, future); + } + + @AllowCopilotExperimental + private static SkillProvider testSkillProvider() { + return new SkillProvider() { + @Override + public CompletableFuture> listSkills() { + return CompletableFuture.completedFuture(List.of()); + } + + @Override + public CompletableFuture readSkill(String name) { + return CompletableFuture.completedFuture(null); + } + }; + } + + @AllowCopilotExperimental + @SuppressWarnings("unchecked") + private static SkillProvider registeredSkillProvider(CopilotClient client, String sessionId) throws Exception { + Field sessionsField = CopilotClient.class.getDeclaredField("sessions"); + sessionsField.setAccessible(true); + Map sessions = (Map) sessionsField.get(client); + CopilotSession session = sessions.get(sessionId); + return session != null ? session.getSkillProvider() : null; } } diff --git a/java/sdk/src/test/java/com/github/copilot/CopilotSessionTest.java b/java/sdk/src/test/java/com/github/copilot/CopilotSessionTest.java index 94ccda7243..4b876f4bee 100644 --- a/java/sdk/src/test/java/com/github/copilot/CopilotSessionTest.java +++ b/java/sdk/src/test/java/com/github/copilot/CopilotSessionTest.java @@ -17,7 +17,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -178,53 +177,6 @@ void testShouldReceiveSessionEvents() throws Exception { } } - /** - * Verifies that send() returns immediately while events stream in background. - * - * @see Snapshot: - * session/send_returns_immediately_while_events_stream_in_background - */ - @Test - void testSendReturnsImmediatelyWhileEventsStreamInBackground() throws Exception { - ctx.configureForTest("session", "send_returns_immediately_while_events_stream_in_background"); - - try (CopilotClient client = ctx.createClient()) { - CopilotSession session = client - .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); - - var events = new ArrayList(); - var lastMessage = new AtomicReference(); - var done = new CompletableFuture(); - - session.on(evt -> { - events.add(evt.getType()); - if (evt instanceof AssistantMessageEvent msg) { - lastMessage.set(msg); - } else if (evt instanceof SessionIdleEvent) { - done.complete(null); - } - }); - - // Use a slow command so we can verify send() returns before completion - // Use String convenience overload (covers send(String) path) - session.send("Run 'sleep 2 && echo done'").get(); - - // At this point, we might not have received session.idle yet - // The event handling happens asynchronously - - // Wait for completion - done.get(60, TimeUnit.SECONDS); - - assertTrue(events.contains("session.idle")); - assertTrue(events.contains("assistant.message")); - assertNotNull(lastMessage.get()); - assertTrue(lastMessage.get().getData().content().contains("done"), - "Response should contain done: " + lastMessage.get().getData().content()); - - session.close(); - } - } - /** * Verifies that sendAndWait blocks until session is idle and returns the final * assistant message. diff --git a/java/sdk/src/test/java/com/github/copilot/EventFidelityTest.java b/java/sdk/src/test/java/com/github/copilot/EventFidelityTest.java index cca63b4d67..ed63d60764 100644 --- a/java/sdk/src/test/java/com/github/copilot/EventFidelityTest.java +++ b/java/sdk/src/test/java/com/github/copilot/EventFidelityTest.java @@ -6,8 +6,8 @@ import static org.junit.jupiter.api.Assertions.*; -import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterAll; @@ -60,7 +60,7 @@ void testShouldEmitAssistantUsageEventAfterModelCall() throws Exception { CopilotSession session = client .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); - List events = new ArrayList<>(); + List events = new CopyOnWriteArrayList<>(); session.on(events::add); session.sendAndWait(new MessageOptions().setPrompt("What is 5+5? Reply with just the number.")).get(60, @@ -94,7 +94,7 @@ void testShouldEmitSessionUsageInfoEventAfterModelCall() throws Exception { CopilotSession session = client .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); - List events = new ArrayList<>(); + List events = new CopyOnWriteArrayList<>(); session.on(events::add); session.sendAndWait(new MessageOptions().setPrompt("What is 5+5? Reply with just the number.")).get(60, diff --git a/java/sdk/src/test/java/com/github/copilot/ForceStopSessionTest.java b/java/sdk/src/test/java/com/github/copilot/ForceStopSessionTest.java new file mode 100644 index 0000000000..1b51dce326 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ForceStopSessionTest.java @@ -0,0 +1,482 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.generated.SessionEvent; +import com.github.copilot.generated.SessionIdleEvent; +import com.github.copilot.rpc.CopilotClientOptions; +import com.github.copilot.rpc.MessageOptions; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.SessionConfig; +import com.github.copilot.rpc.SessionHooks; + +class ForceStopSessionTest { + + @CopilotResponse + public record Answer(int value) { + } + + @Test + void gracefulClosePreservesEventsAndSessionEndHooksUntilDetachCompletes() throws Exception { + var events = new AtomicInteger(); + var hooks = new AtomicInteger(); + try (var server = new Server(); var client = server.createClient()) { + server.emitDetachCallbacks = true; + var session = client.createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setHooks(new SessionHooks().setOnSessionEnd((input, invocation) -> { + assertEquals("user_exit", input.reason()); + hooks.incrementAndGet(); + return CompletableFuture.completedFuture(null); + }))).get(5, TimeUnit.SECONDS); + try (var subscription = session.on(event -> events.incrementAndGet())) { + session.close(); + assertEquals(1, server.detaches.get()); + assertEquals(1, hooks.get(), "Ordinary detach must still invoke the session-end hook"); + assertEquals(1, events.get(), "Ordinary detach must deliver events before callback retirement"); + server.emitIdle(session.getSessionId()); + client.ping("retired detach callbacks barrier").get(5, TimeUnit.SECONDS); + assertEquals(1, events.get(), "Detach completion must retire subsequent callbacks"); + assertThrows(IllegalStateException.class, () -> session.on(event -> fail("Retired handler"))); + session.close(); + assertEquals(1, server.detaches.get(), "Repeated close must not detach again"); + } + } + } + + @Test + void forceStopTerminatesRetainedSessionsWithoutDetachRpc() throws Exception { + try (var server = new Server(); var client = server.createClient()) { + var session = createSession(client); + client.forceStop().get(5, TimeUnit.SECONDS); + + assertThrows(IllegalStateException.class, () -> session.on(event -> fail("Retired handler"))); + assertThrows(IllegalStateException.class, + () -> session.on(SessionIdleEvent.class, event -> fail("Retired typed handler"))); + assertThrows(IllegalStateException.class, () -> session.send("after force stop")); + session.close(); + assertEquals(0, server.detaches.get(), "Local retirement must not detach over RPC"); + } + } + + @Test + void forceStopDropsRemainingSnapshotHandlersWithoutJoiningAnAdmittedHandler() throws Exception { + var entered = new CountDownLatch(1); + var release = new CountDownLatch(1); + var dispatchThread = new AtomicReference(); + var laterCalls = new AtomicInteger(); + try (var server = new Server(); var client = server.createClient()) { + var session = createSession(client); + try (var first = session.on(event -> { + dispatchThread.set(Thread.currentThread()); + entered.countDown(); + awaitRelease(release); + }); var second = session.on(event -> laterCalls.incrementAndGet())) { + try { + server.emitIdle(session.getSessionId()); + assertTrue(entered.await(5, TimeUnit.SECONDS), "The first handler must be admitted"); + client.forceStop().get(5, TimeUnit.SECONDS); + assertEquals(1, release.getCount(), "Force stop must not require releasing the user handler"); + } finally { + release.countDown(); + Thread thread = dispatchThread.get(); + if (thread != null) { + thread.join(5000); + assertFalse(thread.isAlive(), "The stopped event reader must finish after its handler"); + } + } + assertEquals(0, laterCalls.get(), "Retirement must discard the retained dispatch snapshot"); + } + } + } + + @Test + void forceStopRetiresErrorCallbacksWhileGracefulCloseAwaitsDetach() throws Exception { + var entered = new CountDownLatch(1); + var release = new CountDownLatch(1); + var dispatchThread = new AtomicReference(); + var errorCalls = new AtomicInteger(); + try (var server = new Server(); var client = server.createClient()) { + server.holdDetachResponse = true; + var session = createSession(client); + session.setEventErrorHandler((event, error) -> errorCalls.incrementAndGet()); + try (var first = session.on(event -> { + dispatchThread.set(Thread.currentThread()); + entered.countDown(); + awaitRelease(release); + throw new IllegalStateException("Admitted handler failed after forced retirement"); + })) { + try { + server.emitIdle(session.getSessionId()); + assertTrue(entered.await(5, TimeUnit.SECONDS), "The first snapshot handler must be admitted"); + var closing = CompletableFuture.runAsync(session::close); + assertTrue(server.detachEntered.await(5, TimeUnit.SECONDS), "Graceful close must await detach"); + // Prevent graceful close's final retirement from masking force escalation. + synchronized (session) { + client.forceStop().get(5, TimeUnit.SECONDS); + assertFalse(closing.isDone(), "Graceful close must still await its final retirement"); + release.countDown(); + Thread thread = dispatchThread.get(); + thread.join(5000); + assertFalse(thread.isAlive(), "The stopped reader must finish its admitted handler"); + assertEquals(0, errorCalls.get(), "Force stop must discard the unadmitted error callback"); + } + var detachError = assertThrows(ExecutionException.class, () -> closing.get(5, TimeUnit.SECONDS)); + assertInstanceOf(IllegalStateException.class, detachError.getCause()); + } finally { + release.countDown(); + Thread thread = dispatchThread.get(); + if (thread != null) { + thread.join(5000); + assertFalse(thread.isAlive(), "The event reader must finish after forced cleanup"); + } + } + } + } + } + + @ParameterizedTest + @CsvSource({"false, false", "false, true", "true, false", "true, true"}) + void retirementPreservesTerminalOutcomeWhileSchedulerHandoffIsBlocked(boolean terminalError, boolean force) + throws Exception { + var entered = new CountDownLatch(1); + var release = new CountDownLatch(1); + try (var server = new Server(); var client = server.createClient()) { + var session = createSession(client); + var occupying = session.sendAndWait(new MessageOptions().setPrompt("occupy scheduler"), 0); + var continuation = occupying.whenComplete((value, error) -> { + entered.countDown(); + awaitRelease(release); + }); + try (var gate = (AutoCloseable) release::countDown) { + client.ping("first send acknowledgment").get(5, TimeUnit.SECONDS); + server.emitIdle(session.getSessionId()); + assertTrue(entered.await(5, TimeUnit.SECONDS), "A continuation must occupy the session scheduler"); + + var completed = session.sendAndWait(new MessageOptions().setPrompt("completed terminal"), 0); + client.ping("second send acknowledgment").get(5, TimeUnit.SECONDS); + if (terminalError) { + server.emitEvent(session.getSessionId(), "session.error", + Map.of("errorType", "query", "message", "original terminal failure")); + } else { + server.emitEvent(session.getSessionId(), "assistant.message", + Map.of("messageId", "terminal-message", "content", "completed before retirement")); + server.emitIdle(session.getSessionId()); + } + client.ping("terminal dispatch acknowledgment").get(5, TimeUnit.SECONDS); + assertFalse(completed.isDone(), "The received terminal outcome must still await scheduler handoff"); + + if (force) { + client.forceStop().get(5, TimeUnit.SECONDS); + } else { + CompletableFuture.runAsync(session::close).get(5, TimeUnit.SECONDS); + } + if (terminalError) { + var error = assertThrows(ExecutionException.class, () -> completed.get(5, TimeUnit.SECONDS)); + assertEquals("Session error: original terminal failure", error.getCause().getMessage()); + } else { + assertEquals("completed before retirement", completed.get(5, TimeUnit.SECONDS).getData().content()); + } + assertEquals(1, release.getCount(), "Retirement must not join the admitted continuation"); + } + continuation.get(5, TimeUnit.SECONDS); + } + } + + @Test + void forceStopFailsAcknowledgedPlainAndStructuredWaitsWithNoTimeout() throws Exception { + var entered = new CountDownLatch(2); + var release = new CountDownLatch(1); + try (var server = new Server(); var client = server.createClient()) { + var session = createSession(client); + var plain = session.sendAndWait(new MessageOptions().setPrompt("plain"), 0); + var structured = session.sendAndWait(new MessageOptions().setPrompt("structured"), Answer.class, 0); + client.ping("send acknowledgment barrier").get(5, TimeUnit.SECONDS); + assertFalse(plain.isDone()); + assertFalse(structured.isDone()); + + var plainContinuation = plain.whenComplete((value, error) -> { + entered.countDown(); + awaitRelease(release); + }); + var structuredContinuation = structured.whenComplete((value, error) -> { + entered.countDown(); + awaitRelease(release); + }); + try (var gate = (AutoCloseable) release::countDown) { + client.forceStop().get(5, TimeUnit.SECONDS); + assertTrue(entered.await(5, TimeUnit.SECONDS), + "Both waits must retire even while their application continuations are blocked"); + var plainError = assertThrows(ExecutionException.class, () -> plain.get(5, TimeUnit.SECONDS)); + var structuredError = assertThrows(ExecutionException.class, () -> structured.get(5, TimeUnit.SECONDS)); + assertInstanceOf(IllegalStateException.class, plainError.getCause()); + assertInstanceOf(IllegalStateException.class, structuredError.getCause()); + assertTrue(plainError.getCause().getMessage().contains("closed")); + assertTrue(structuredError.getCause().getMessage().contains("closed")); + } + assertThrows(ExecutionException.class, () -> plainContinuation.get(5, TimeUnit.SECONDS)); + assertThrows(ExecutionException.class, () -> structuredContinuation.get(5, TimeUnit.SECONDS)); + } + } + + @Test + void gracefulCloseFailsAcknowledgedWaitsWithoutJoiningTheirContinuations() throws Exception { + var entered = new CountDownLatch(2); + var release = new CountDownLatch(1); + try (var server = new Server(); var client = server.createClient()) { + var session = createSession(client); + var plain = session.sendAndWait(new MessageOptions().setPrompt("plain"), 0); + var structured = session.sendAndWait(new MessageOptions().setPrompt("structured"), Answer.class, 0); + client.ping("send acknowledgment barrier").get(5, TimeUnit.SECONDS); + assertFalse(plain.isDone()); + assertFalse(structured.isDone()); + + var plainContinuation = plain.whenComplete((value, error) -> { + entered.countDown(); + awaitRelease(release); + }); + var structuredContinuation = structured.whenComplete((value, error) -> { + entered.countDown(); + awaitRelease(release); + }); + try (var gate = (AutoCloseable) release::countDown) { + CompletableFuture.runAsync(session::close).get(5, TimeUnit.SECONDS); + assertTrue(entered.await(5, TimeUnit.SECONDS), "Retirement must admit both blocked continuations"); + var plainError = assertThrows(ExecutionException.class, () -> plain.get(5, TimeUnit.SECONDS)); + var structuredError = assertThrows(ExecutionException.class, () -> structured.get(5, TimeUnit.SECONDS)); + assertInstanceOf(IllegalStateException.class, plainError.getCause()); + assertInstanceOf(IllegalStateException.class, structuredError.getCause()); + assertEquals("Session closed before response completed", plainError.getCause().getMessage()); + assertEquals("Session closed before response completed", structuredError.getCause().getMessage()); + assertEquals(1, server.detaches.get(), "Graceful close must still acknowledge one detach"); + session.close(); + assertEquals(1, server.detaches.get(), "Repeated close must not detach again"); + } + assertThrows(ExecutionException.class, () -> plainContinuation.get(5, TimeUnit.SECONDS)); + assertThrows(ExecutionException.class, () -> structuredContinuation.get(5, TimeUnit.SECONDS)); + } + } + + @Test + void duplicateUntypedSubscriptionsStillDispatchOnceAndShareUnsubscription() throws Exception { + var calls = new AtomicInteger(); + try (var server = new Server(); var client = server.createClient()) { + var session = createSession(client); + Consumer handler = event -> calls.incrementAndGet(); + try (var first = session.on(handler); var second = session.on(handler)) { + server.emitIdle(session.getSessionId()); + client.ping("first dispatch barrier").get(5, TimeUnit.SECONDS); + assertEquals(1, calls.get()); + + first.close(); + server.emitIdle(session.getSessionId()); + client.ping("unsubscription barrier").get(5, TimeUnit.SECONDS); + assertEquals(1, calls.get()); + client.forceStop().get(5, TimeUnit.SECONDS); + } + } + } + + private static CopilotSession createSession(CopilotClient client) throws Exception { + return client.createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(5, + TimeUnit.SECONDS); + } + + private static void awaitRelease(CountDownLatch release) { + boolean interrupted = false; + try { + while (true) { + try { + release.await(); + return; + } catch (InterruptedException error) { + interrupted = true; + } + } + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + private static final class Server implements AutoCloseable { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + private final ServerSocket listener; + private final Thread thread; + private final AtomicInteger detaches = new AtomicInteger(); + private final CountDownLatch detachEntered = new CountDownLatch(1); + private volatile Socket socket; + private volatile boolean closed; + private volatile boolean emitDetachCallbacks; + private volatile boolean holdDetachResponse; + private volatile Exception failure; + + Server() throws IOException { + listener = new ServerSocket(0, 1, InetAddress.getByName("127.0.0.1")); + thread = new Thread(this::serve, "force-stop-session-server"); + thread.setDaemon(true); + thread.start(); + } + + CopilotClient createClient() { + return new CopilotClient(new CopilotClientOptions().setCliUrl("127.0.0.1:" + listener.getLocalPort())); + } + + void emitIdle(String sessionId) throws IOException { + emitEvent(sessionId, "session.idle", Map.of()); + } + + void emitEvent(String sessionId, String type, Map data) throws IOException { + assertNotNull(socket, "Session creation must establish the connection before events"); + writeMessage(Map.of("jsonrpc", "2.0", "method", "session.event", "params", + Map.of("sessionId", sessionId, "event", + Map.of("type", type, "id", "00000000-0000-0000-0000-000000000001", "timestamp", + "2026-10-05T00:00:00Z", "data", data)))); + } + + private void serve() { + try (Socket accepted = listener.accept()) { + socket = accepted; + JsonNode pendingDetachId = null; + while (!closed) { + JsonNode request = readMessage(accepted.getInputStream()); + if (request == null) { + return; + } + JsonNode params = request.path("params"); + if (holdDetachResponse && request.path("method").asText().equals("session.detach")) { + detaches.incrementAndGet(); + detachEntered.countDown(); + continue; + } + if (!request.has("method") && request.path("id").asLong() == 9_000_000L) { + if (pendingDetachId == null || request.has("error")) { + throw new IOException("Unexpected detach hook response: " + request); + } + var response = MAPPER.createObjectNode().put("jsonrpc", "2.0"); + response.set("id", pendingDetachId); + response.set("result", MAPPER.valueToTree(Map.of("success", true))); + pendingDetachId = null; + writeMessage(response); + continue; + } + if (emitDetachCallbacks && request.path("method").asText().equals("session.detach")) { + detaches.incrementAndGet(); + pendingDetachId = request.get("id"); + String sessionId = params.path("sessionId").asText(); + emitIdle(sessionId); + var hookInput = Map.of("sessionId", sessionId, "timestamp", 0, "cwd", ".", "reason", + "user_exit"); + writeMessage(Map.of("jsonrpc", "2.0", "id", 9_000_000L, "method", "hooks.invoke", "params", + Map.of("sessionId", sessionId, "hookType", "sessionEnd", "input", hookInput))); + continue; + } + Object result = switch (request.path("method").asText()) { + case "connect" -> Map.of("protocolVersion", SdkProtocolVersion.get()); + case "session.create" -> Map.of("sessionId", params.path("sessionId").asText()); + case "session.send" -> Map.of("messageId", params.path("prompt").asText()); + case "session.detach" -> { + detaches.incrementAndGet(); + yield Map.of("success", true); + } + case "ping" -> Map.of("message", params.path("message").asText(), "protocolVersion", + SdkProtocolVersion.get()); + default -> throw new IOException("Unexpected RPC: " + request.path("method")); + }; + var response = MAPPER.createObjectNode().put("jsonrpc", "2.0"); + response.set("id", request.get("id")); + response.set("result", MAPPER.valueToTree(result)); + writeMessage(response); + } + } catch (Exception error) { + if (!closed) { + failure = error; + } + } + } + + private static JsonNode readMessage(InputStream input) throws IOException { + var header = new StringBuilder(); + while (!header.toString().endsWith("\r\n\r\n")) { + int value = input.read(); + if (value < 0) { + if (header.isEmpty()) { + return null; + } + throw new IOException("Truncated RPC header"); + } + header.append((char) value); + if (header.length() > 8192) { + throw new IOException("Oversized RPC header"); + } + } + int length; + try { + length = Integer.parseInt(header.substring(header.indexOf(":") + 1).trim()); + } catch (NumberFormatException error) { + throw new IOException("Invalid Content-Length", error); + } + if (length < 0 || length > 1_048_576) { + throw new IOException("Invalid Content-Length: " + length); + } + byte[] body = input.readNBytes(length); + if (body.length != length) { + throw new IOException("Truncated RPC body"); + } + return MAPPER.readTree(body); + } + + private synchronized void writeMessage(Object message) throws IOException { + byte[] body = MAPPER.writeValueAsBytes(message); + var output = socket.getOutputStream(); + output.write(("Content-Length: " + body.length + "\r\n\r\n").getBytes(StandardCharsets.UTF_8)); + output.write(body); + output.flush(); + } + + @Override + public void close() throws Exception { + closed = true; + listener.close(); + if (socket != null) { + socket.close(); + } + thread.join(5000); + assertFalse(thread.isAlive(), "The RPC fixture must stop"); + assertNull(failure, "RPC fixture failure: " + failure); + } + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/ImageGenerationConfigTest.java b/java/sdk/src/test/java/com/github/copilot/ImageGenerationConfigTest.java new file mode 100644 index 0000000000..5acace1e7a --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ImageGenerationConfigTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.copilot.rpc.ImageGenerationConfig; +import com.github.copilot.rpc.ResumeSessionConfig; +import com.github.copilot.rpc.SessionConfig; + +class ImageGenerationConfigTest { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void createAndResumeForwardExplicitOptIn(boolean enabled) { + var imageGeneration = new ImageGenerationConfig().setEnabled(enabled); + var createConfig = new SessionConfig().setImageGeneration(imageGeneration).clone(); + var resumeConfig = new ResumeSessionConfig().setImageGeneration(imageGeneration).clone(); + assertSame(imageGeneration, createConfig.getImageGeneration()); + assertSame(imageGeneration, resumeConfig.getImageGeneration()); + var create = SessionRequestBuilder.buildCreateRequest(createConfig, "image-test"); + var resume = SessionRequestBuilder.buildResumeRequest("image-test", resumeConfig); + for (Object request : new Object[]{create, resume}) { + JsonNode json = JsonRpcClient.getObjectMapper().valueToTree(request); + assertEquals(enabled, json.get("imageGeneration").get("enabled").booleanValue()); + assertEquals(1, json.get("imageGeneration").size()); + } + } + + @Test + void createAndResumeOmitUnsetOptIn() { + var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig(), "image-test"); + var resume = SessionRequestBuilder.buildResumeRequest("image-test", new ResumeSessionConfig()); + for (Object request : new Object[]{create, resume}) { + JsonNode json = JsonRpcClient.getObjectMapper().valueToTree(request); + assertFalse(json.has("imageGeneration")); + } + JsonNode empty = JsonRpcClient.getObjectMapper().valueToTree(new ImageGenerationConfig()); + assertEquals(0, empty.size()); + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/ManagedSettingsTest.java b/java/sdk/src/test/java/com/github/copilot/ManagedSettingsTest.java index d6341b26c5..7b1910753f 100644 --- a/java/sdk/src/test/java/com/github/copilot/ManagedSettingsTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ManagedSettingsTest.java @@ -23,22 +23,29 @@ class ManagedSettingsTest { @Test void forwardsManagedSettingsOnCreateAndResume() throws Exception { var permissions = new ManagedSettingsPermissions() - .setDisableBypassPermissionsMode(DisableBypassPermissionsModes.DISABLE).setDeny(List.of("Shell(rm *)")) - .setAsk(List.of("Domain(publish.example)")).setAllow(List.of("Read(**)")); + .setDisableBypassPermissionsMode(DisableBypassPermissionsModes.DISABLE) + .setDisableAssistedPermissionsMode(true).setDeny(List.of("Shell(rm *)")) + .setAsk(List.of("Domain(publish.example)")).setAllow(List.of("Read(**)")) + .setLimitTo(List.of("Domain(github.com)")); var managedSettings = new ManagedSettings().setPermissions(permissions); - var create = SessionRequestBuilder.buildCreateRequest( - new SessionConfig().setEnableManagedSettings(true).setManagedSettings(managedSettings), - "managed-create"); + var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setEnableManagedSettings(true) + .setEnforceManagedModelDefaults(true).setManagedSettings(managedSettings), "managed-create"); var resume = SessionRequestBuilder.buildResumeRequest("managed-resume", - new ResumeSessionConfig().setEnableManagedSettings(true).setManagedSettings(managedSettings)); + new ResumeSessionConfig().setEnableManagedSettings(true).setEnforceManagedModelDefaults(true) + .setManagedSettings(managedSettings)); assertEquals(managedSettings, create.getManagedSettings()); assertEquals(managedSettings, resume.getManagedSettings()); + assertTrue(create.getEnforceManagedModelDefaults()); + assertTrue(resume.getEnforceManagedModelDefaults()); var json = new ObjectMapper().writeValueAsString(create); assertTrue(json.contains("\"enableManagedSettings\":true")); + assertTrue(json.contains("\"enforceManagedModelDefaults\":true")); assertTrue(json.contains("\"managedSettings\":{\"permissions\"")); assertTrue(json.contains("\"disableBypassPermissionsMode\":\"disable\"")); + assertTrue(json.contains("\"limitTo\":[\"Domain(github.com)\"]")); + assertTrue(json.contains("\"disableAssistedPermissionsMode\":true")); } @Test @@ -55,7 +62,8 @@ void preservesExplicitEmptyPermissionArrays() throws Exception { // absent (null) list imposes no such restriction. Jackson NON_NULL must // emit an explicit empty array as `[]` and omit null fields, so the two // remain distinguishable on the wire. - var permissions = new ManagedSettingsPermissions().setDeny(List.of()).setAsk(List.of()).setAllow(List.of()); + var permissions = new ManagedSettingsPermissions().setDeny(List.of()).setAsk(List.of()).setAllow(List.of()) + .setLimitTo(List.of()); var managedSettings = new ManagedSettings().setPermissions(permissions); var create = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setManagedSettings(managedSettings), "managed-empty"); @@ -64,6 +72,7 @@ void preservesExplicitEmptyPermissionArrays() throws Exception { assertTrue(json.contains("\"deny\":[]"), json); assertTrue(json.contains("\"ask\":[]"), json); assertTrue(json.contains("\"allow\":[]"), json); + assertTrue(json.contains("\"limitTo\":[]"), json); } @Test diff --git a/java/sdk/src/test/java/com/github/copilot/MultiProviderConfigTest.java b/java/sdk/src/test/java/com/github/copilot/MultiProviderConfigTest.java index 171e525cf3..fc6dba15f4 100644 --- a/java/sdk/src/test/java/com/github/copilot/MultiProviderConfigTest.java +++ b/java/sdk/src/test/java/com/github/copilot/MultiProviderConfigTest.java @@ -39,6 +39,7 @@ void testNamedProviderConfigDefaultsAreNull() { assertNull(provider.getName()); assertNull(provider.getType()); assertNull(provider.getWireApi()); + assertNull(provider.getModelProvider()); assertNull(provider.getBaseUrl()); assertNull(provider.getApiKey()); assertNull(provider.getBearerToken()); @@ -51,8 +52,8 @@ void testNamedProviderConfigFluentSettersReturnSameInstance() { var provider = new NamedProviderConfig(); NamedProviderConfig result = provider.setName("my-openai").setType("openai").setWireApi("responses") - .setBaseUrl("https://api.openai.com/v1").setApiKey("sk-test").setBearerToken("bearer") - .setAzure(new AzureOptions()).setHeaders(Map.of("X-Custom", "v")); + .setModelProvider("ollama").setBaseUrl("https://api.openai.com/v1").setApiKey("sk-test") + .setBearerToken("bearer").setAzure(new AzureOptions()).setHeaders(Map.of("X-Custom", "v")); assertEquals(provider, result); } @@ -60,13 +61,14 @@ void testNamedProviderConfigFluentSettersReturnSameInstance() { @Test void testSerializeNamedProviderConfig() throws Exception { var provider = new NamedProviderConfig().setName("my-openai").setType("openai").setWireApi("responses") - .setBaseUrl("https://api.openai.com/v1").setApiKey("sk-test"); + .setModelProvider("ollama").setBaseUrl("https://api.openai.com/v1").setApiKey("sk-test"); JsonNode json = MAPPER.valueToTree(provider); assertEquals("my-openai", json.get("name").asText()); assertEquals("openai", json.get("type").asText()); assertEquals("responses", json.get("wireApi").asText()); + assertEquals("ollama", json.get("modelProvider").asText()); assertEquals("https://api.openai.com/v1", json.get("baseUrl").asText()); assertEquals("sk-test", json.get("apiKey").asText()); // Null fields must be omitted (NON_NULL) diff --git a/java/sdk/src/test/java/com/github/copilot/PackagedConsumerIT.java b/java/sdk/src/test/java/com/github/copilot/PackagedConsumerIT.java new file mode 100644 index 0000000000..29cb03f487 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/PackagedConsumerIT.java @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import javax.tools.ToolProvider; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class PackagedConsumerIT { + + @Test + void runsStandaloneConsumerWithManifestClasspath(@TempDir Path directory) throws Exception { + Path build = Path.of(System.getProperty("project.build.directory")); + Path library = Files.createDirectory(directory.resolve("lib")); + var dependencies = new ArrayList(); + dependencies.add(build.resolve(System.getProperty("project.build.finalName") + ".jar")); + try (var jars = Files.list(build.resolve("consumer-dependencies"))) { + dependencies.addAll(jars.filter(path -> path.toString().endsWith(".jar")).sorted().toList()); + } + for (Path dependency : dependencies) { + Files.copy(dependency, library.resolve(dependency.getFileName())); + } + + Path source = directory.resolve("Consumer.java"); + Files.writeString(source, """ + import com.github.copilot.CopilotClient; + import com.github.copilot.ConnectionState; + import com.github.copilot.rpc.MessageOptions; + import com.fasterxml.jackson.databind.ObjectMapper; + import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + + public class Consumer { + public static void main(String[] args) throws Exception { + try (var client = new CopilotClient()) { + var mapper = new ObjectMapper().registerModule(new JavaTimeModule()); + var json = mapper.writeValueAsString(new MessageOptions().setPrompt("consumer")); + if (!mapper.readTree(json).get("prompt").asText().equals("consumer") + || client.getState() != ConnectionState.DISCONNECTED) { + throw new AssertionError("Packaged SDK consumer failed"); + } + } + System.out.println("consumer-ok"); + } + } + """); + + String classpath = String.join(File.pathSeparator, + dependencies.stream().map(path -> library.resolve(path.getFileName()).toString()).toList()); + var diagnostics = new ByteArrayOutputStream(); + int compilation = ToolProvider.getSystemJavaCompiler().run(null, diagnostics, diagnostics, "--release", "17", + "-proc:none", "-classpath", classpath, source.toString()); + assertEquals(0, compilation, diagnostics.toString(StandardCharsets.UTF_8)); + + var manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + manifest.getMainAttributes().put(Attributes.Name.MAIN_CLASS, "Consumer"); + manifest.getMainAttributes().put(Attributes.Name.CLASS_PATH, + String.join(" ", dependencies.stream().map(path -> "lib/" + path.getFileName()).toList())); + Path consumerJar = directory.resolve("consumer.jar"); + try (var jar = new JarOutputStream(Files.newOutputStream(consumerJar), manifest)) { + jar.putNextEntry(new JarEntry("Consumer.class")); + Files.copy(directory.resolve("Consumer.class"), jar); + jar.closeEntry(); + } + Files.delete(directory.resolve("Consumer.class")); + + Path java = Path.of(System.getProperty("java.home"), "bin", + System.getProperty("os.name").startsWith("Windows") ? "java.exe" : "java"); + Path output = directory.resolve("output.log"); + ProcessBuilder builder = new ProcessBuilder(java.toString(), "-jar", consumerJar.toString()) + .directory(directory.toFile()).redirectErrorStream(true).redirectOutput(output.toFile()); + builder.environment().remove("COPILOT_SDK_DEFAULT_CONNECTION"); + Process process = builder.start(); + try { + boolean exited = process.waitFor(30, TimeUnit.SECONDS); + String text = Files.readString(output); + assertTrue(exited, "Consumer JVM did not exit. Output:\n" + text); + assertEquals(0, process.exitValue(), text); + assertTrue(text.contains("consumer-ok"), text); + } finally { + if (process.isAlive()) { + process.destroyForcibly().waitFor(30, TimeUnit.SECONDS); + } + } + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/ProviderConfigTest.java b/java/sdk/src/test/java/com/github/copilot/ProviderConfigTest.java index effb360401..e5836a0c1b 100644 --- a/java/sdk/src/test/java/com/github/copilot/ProviderConfigTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ProviderConfigTest.java @@ -42,6 +42,7 @@ void testDefaultsAreNull() { assertNull(provider.getType()); assertNull(provider.getWireApi()); + assertNull(provider.getModelProvider()); assertNull(provider.getBaseUrl()); assertNull(provider.getApiKey()); assertNull(provider.getBearerToken()); @@ -52,7 +53,7 @@ void testDefaultsAreNull() { void testFluentSettersReturnSameInstance() { var provider = new ProviderConfig(); - ProviderConfig result = provider.setType("openai").setWireApi("completions") + ProviderConfig result = provider.setType("openai").setWireApi("completions").setModelProvider("lm_studio") .setBaseUrl("https://api.openai.com/v1").setApiKey("sk-test-key").setBearerToken("bearer-token") .setAzure(new AzureOptions()); @@ -63,12 +64,13 @@ void testFluentSettersReturnSameInstance() { @Test void testGettersReturnSetValues() { var azure = new AzureOptions().setApiVersion("2024-02-01"); - var provider = new ProviderConfig().setType("azure-openai").setWireApi("chat") + var provider = new ProviderConfig().setType("azure-openai").setWireApi("chat").setModelProvider("azure_openai") .setBaseUrl("https://my-resource.openai.azure.com").setApiKey("my-key").setBearerToken("my-token") .setAzure(azure); assertEquals("azure-openai", provider.getType()); assertEquals("chat", provider.getWireApi()); + assertEquals("azure_openai", provider.getModelProvider()); assertEquals("https://my-resource.openai.azure.com", provider.getBaseUrl()); assertEquals("my-key", provider.getApiKey()); assertEquals("my-token", provider.getBearerToken()); @@ -253,18 +255,20 @@ void testTransportOmittedWhenNull() throws Exception { @Test void testSerializeAllFields() throws Exception { var provider = new ProviderConfig().setType("azure-openai").setWireApi("completions") - .setBaseUrl("https://my-resource.openai.azure.com").setApiKey("my-api-key") - .setBearerToken("my-bearer-token").setAzure(new AzureOptions().setApiVersion("2024-02-01")); + .setModelProvider("azure_openai").setBaseUrl("https://my-resource.openai.azure.com") + .setApiKey("my-api-key").setBearerToken("my-bearer-token") + .setAzure(new AzureOptions().setApiVersion("2024-02-01")); JsonNode json = MAPPER.valueToTree(provider); assertEquals("azure-openai", json.get("type").asText()); assertEquals("completions", json.get("wireApi").asText()); + assertEquals("azure_openai", json.get("modelProvider").asText()); assertEquals("https://my-resource.openai.azure.com", json.get("baseUrl").asText()); assertEquals("my-api-key", json.get("apiKey").asText()); assertEquals("my-bearer-token", json.get("bearerToken").asText()); assertEquals("2024-02-01", json.get("azure").get("apiVersion").asText()); - assertEquals(6, json.size(), "Expected exactly 6 JSON fields"); + assertEquals(7, json.size(), "Expected exactly 7 JSON fields"); } @Test @@ -417,7 +421,8 @@ void testResumeSessionConfigWithoutProviderOmitsField() throws Exception { void testProviderModelIdAndWireModelSerialization() throws Exception { var provider = new ProviderConfig().setBaseUrl("https://example.com/provider") .setHeaders(java.util.Map.of("Authorization", "Bearer provider-token")).setModelId("gpt-4o") - .setWireModel("my-finetune-v3").setMaxPromptTokens(100_000).setMaxOutputTokens(4096); + .setWireModel("my-finetune-v3").setModelProvider("lm_studio").setMaxPromptTokens(100_000) + .setMaxOutputTokens(4096); JsonNode json = MAPPER.valueToTree(provider); @@ -425,6 +430,7 @@ void testProviderModelIdAndWireModelSerialization() throws Exception { assertEquals("Bearer provider-token", json.get("headers").get("Authorization").asText()); assertEquals("gpt-4o", json.get("modelId").asText()); assertEquals("my-finetune-v3", json.get("wireModel").asText()); + assertEquals("lm_studio", json.get("modelProvider").asText()); assertEquals(100_000, json.get("maxPromptTokens").asInt()); assertEquals(4096, json.get("maxOutputTokens").asInt()); @@ -432,6 +438,7 @@ void testProviderModelIdAndWireModelSerialization() throws Exception { ProviderConfig deserialized = MAPPER.readValue(MAPPER.writeValueAsString(provider), ProviderConfig.class); assertEquals("gpt-4o", deserialized.getModelId()); assertEquals("my-finetune-v3", deserialized.getWireModel()); + assertEquals("lm_studio", deserialized.getModelProvider()); assertEquals(100_000, deserialized.getMaxPromptTokens().getAsInt()); assertEquals(4096, deserialized.getMaxOutputTokens().getAsInt()); } @@ -441,6 +448,7 @@ void testProviderModelFieldsDefaultToNull() { var provider = new ProviderConfig(); assertNull(provider.getModelId()); assertNull(provider.getWireModel()); + assertNull(provider.getModelProvider()); assertTrue(provider.getMaxPromptTokens().isEmpty()); assertTrue(provider.getMaxOutputTokens().isEmpty()); } @@ -453,6 +461,7 @@ void testProviderModelFieldsOmittedWhenNull() throws Exception { assertTrue(json.path("modelId").isMissingNode()); assertTrue(json.path("wireModel").isMissingNode()); + assertTrue(json.path("modelProvider").isMissingNode()); assertTrue(json.path("maxPromptTokens").isMissingNode()); assertTrue(json.path("maxOutputTokens").isMissingNode()); } 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 44bff36444..a92a4b561d 100644 --- a/java/sdk/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java +++ b/java/sdk/src/test/java/com/github/copilot/RpcHandlerDispatcherTest.java @@ -16,8 +16,13 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -43,6 +48,7 @@ * identified by JaCoCo: unknown sessions, missing fields, error paths, and edge * cases for each handler method. */ +@AllowCopilotExperimental class RpcHandlerDispatcherTest { private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); @@ -509,6 +515,213 @@ void userInputRequestHandlerFails() throws Exception { assertEquals(-32603, response.get("error").get("code").asInt()); } + // ===== skillProvider.* tests ===== + + @Test + void skillProviderListAndReadSuccess() throws Exception { + CopilotSession session = createSession("s1"); + session.registerSkillProvider(skillProvider( + () -> CompletableFuture.completedFuture( + List.of(new SkillProviderDescriptor("docs", "Documentation", null, null, "topic"))), + name -> CompletableFuture.completedFuture("# " + name))); + + ObjectNode listParams = MAPPER.createObjectNode(); + listParams.put("sessionId", "s1"); + invokeHandler("skillProvider.list", "60", listParams); + + JsonNode listResponse = readResponse(); + JsonNode descriptor = listResponse.at("/result/skills/0"); + assertEquals("docs", descriptor.path("name").asText()); + assertEquals("Documentation", descriptor.path("description").asText()); + assertEquals("topic", descriptor.path("argumentHint").asText()); + assertFalse(descriptor.has("userInvocable")); + assertFalse(descriptor.has("disableModelInvocation")); + + ObjectNode readParams = MAPPER.createObjectNode(); + readParams.put("sessionId", "s1"); + readParams.put("name", "docs"); + invokeHandler("skillProvider.read", "61", readParams); + + JsonNode readResponse = readResponse(); + assertEquals("# docs", readResponse.at("/result/markdown").asText()); + } + + @Test + void skillProviderNullListReturnsEmptyList() throws Exception { + CopilotSession session = createSession("s1"); + session.registerSkillProvider(skillProvider(() -> CompletableFuture.completedFuture(null), + name -> CompletableFuture.completedFuture("# " + name))); + + ObjectNode params = MAPPER.createObjectNode(); + params.put("sessionId", "s1"); + invokeHandler("skillProvider.list", "62", params); + + JsonNode response = readResponse(); + assertTrue(response.at("/result/skills").isArray()); + assertEquals(0, response.at("/result/skills").size()); + } + + @Test + void skillProviderReadNullReturnsNullMarkdown() throws Exception { + CopilotSession session = createSession("s1"); + session.registerSkillProvider(skillProvider(() -> CompletableFuture.completedFuture(List.of()), + name -> CompletableFuture.completedFuture(null))); + + ObjectNode params = MAPPER.createObjectNode(); + params.put("sessionId", "s1"); + params.put("name", "missing"); + invokeHandler("skillProvider.read", "63", params); + + JsonNode response = readResponse(); + assertNull(response.get("error")); + assertTrue(response.at("/result").has("markdown")); + assertTrue(response.at("/result/markdown").isNull()); + } + + @Test + void skillProviderFailuresUseStableMessagesWithoutData() throws Exception { + CopilotSession session = createSession("s1"); + session.registerSkillProvider(skillProvider( + () -> CompletableFuture.failedFuture(new IllegalStateException("secret list failure")), name -> { + throw new IllegalStateException("secret read failure"); + })); + + ObjectNode listParams = MAPPER.createObjectNode(); + listParams.put("sessionId", "s1"); + invokeHandler("skillProvider.list", "64", listParams); + JsonNode listResponse = readResponse(); + assertEquals(-32603, listResponse.at("/error/code").asInt()); + assertEquals("Skill provider listSkills failed", listResponse.at("/error/message").asText()); + assertFalse(listResponse.path("error").has("data")); + + ObjectNode readParams = MAPPER.createObjectNode(); + readParams.put("sessionId", "s1"); + readParams.put("name", "docs"); + invokeHandler("skillProvider.read", "65", readParams); + JsonNode readResponse = readResponse(); + assertEquals(-32603, readResponse.at("/error/code").asInt()); + assertEquals("Skill provider readSkill failed", readResponse.at("/error/message").asText()); + assertFalse(readResponse.path("error").has("data")); + } + + @Test + void skillProviderErrorsForUnknownMissingOrClearedProviderHaveNoData() throws Exception { + ObjectNode unknownParams = MAPPER.createObjectNode(); + unknownParams.put("sessionId", "missing-session"); + invokeHandler("skillProvider.list", "66", unknownParams); + JsonNode unknownResponse = readResponse(); + assertEquals(-32603, unknownResponse.at("/error/code").asInt()); + assertEquals("No skill provider for session: missing-session", unknownResponse.at("/error/message").asText()); + assertFalse(unknownResponse.path("error").has("data")); + + createSession("s1"); + ObjectNode noProviderParams = MAPPER.createObjectNode(); + noProviderParams.put("sessionId", "s1"); + noProviderParams.put("name", "docs"); + invokeHandler("skillProvider.read", "67", noProviderParams); + JsonNode noProviderResponse = readResponse(); + assertEquals(-32603, noProviderResponse.at("/error/code").asInt()); + assertEquals("No skill provider for session: s1", noProviderResponse.at("/error/message").asText()); + assertFalse(noProviderResponse.path("error").has("data")); + + CopilotSession session = sessions.get("s1"); + session.registerSkillProvider(skillProvider(() -> CompletableFuture.completedFuture(List.of()), + name -> CompletableFuture.completedFuture("# " + name))); + session.clearSkillProvider(); + invokeHandler("skillProvider.list", "68", noProviderParams); + JsonNode clearedResponse = readResponse(); + assertEquals(-32603, clearedResponse.at("/error/code").asInt()); + assertEquals("No skill provider for session: s1", clearedResponse.at("/error/message").asText()); + assertFalse(clearedResponse.path("error").has("data")); + } + + @Test + void skillProviderCallIsCancelledByCancelRequest() throws Exception { + CopilotSession session = createSession("s1"); + var entered = new CountDownLatch(1); + var pending = new CompletableFuture>(); + session.registerSkillProvider(skillProvider(() -> { + entered.countDown(); + return pending; + }, name -> CompletableFuture.completedFuture(null))); + + ObjectNode params = MAPPER.createObjectNode(); + params.put("sessionId", "s1"); + invokeHandler("skillProvider.list", "69", params); + assertTrue(entered.await(5, TimeUnit.SECONDS)); + dispatcher.handleCancelRequest(MAPPER.createObjectNode().put("id", 69)); + + JsonNode response = readResponse(); + assertEquals(69, response.path("id").asInt()); + assertEquals(-32800, response.at("/error/code").asInt()); + assertTrue(pending.isCancelled()); + } + + @Test + void skillProviderCallCancelledBeforeDispatchIsNotStarted() throws Exception { + CopilotSession session = createSession("s1"); + var calls = new AtomicInteger(); + session.registerSkillProvider(skillProvider(() -> { + calls.incrementAndGet(); + return CompletableFuture.completedFuture(List.of()); + }, name -> CompletableFuture.completedFuture(null))); + var blockedExecutor = new CompletableFuture(); + var cancellingDispatcher = new RpcHandlerDispatcher(sessions, lifecycleEvents::add, + task -> blockedExecutor.thenRun(task), gitHubTokenProviders); + cancellingDispatcher.registerHandlers(rpc); + + ObjectNode params = MAPPER.createObjectNode(); + params.put("sessionId", "s1"); + invokeHandler("skillProvider.list", "70", params); + cancellingDispatcher.handleCancelRequest(MAPPER.createObjectNode().put("id", 70)); + blockedExecutor.complete(null); + + assertEquals(-32800, readResponse().at("/error/code").asInt()); + assertEquals(0, calls.get()); + } + + @Test + void skillProviderCallsAreCancelledWhenTheConnectionCloses() throws Exception { + CopilotSession session = createSession("s1"); + var entered = new CountDownLatch(1); + var pending = new CompletableFuture(); + session.registerSkillProvider(skillProvider(() -> CompletableFuture.completedFuture(List.of()), name -> { + entered.countDown(); + return pending; + })); + + ObjectNode params = MAPPER.createObjectNode(); + params.put("sessionId", "s1"); + params.put("name", "docs"); + invokeHandler("skillProvider.read", "71", params); + assertTrue(entered.await(5, TimeUnit.SECONDS)); + dispatcher.cancelSkillProviderCalls(); + + assertEquals(-32800, readResponse().at("/error/code").asInt()); + assertTrue(pending.isCancelled()); + } + + @Test + void cancelRequestForAnotherRequestLeavesSkillProviderCallRunning() throws Exception { + CopilotSession session = createSession("s1"); + var entered = new CountDownLatch(1); + var pending = new CompletableFuture(); + session.registerSkillProvider(skillProvider(() -> CompletableFuture.completedFuture(List.of()), name -> { + entered.countDown(); + return pending; + })); + + ObjectNode params = MAPPER.createObjectNode(); + params.put("sessionId", "s1"); + params.put("name", "docs"); + invokeHandler("skillProvider.read", "72", params); + assertTrue(entered.await(5, TimeUnit.SECONDS)); + dispatcher.handleCancelRequest(MAPPER.createObjectNode().put("id", 73)); + pending.complete("# docs"); + + assertEquals("# docs", readResponse().at("/result/markdown").asText()); + } + // ===== hooks.invoke tests ===== @Test @@ -649,4 +862,19 @@ void systemMessageTransformWithKnownSessionNoCallbacks() throws Exception { assertNotNull(resultSections); assertEquals("Original content", resultSections.get("identity").get("content").asText()); } + + private static SkillProvider skillProvider(Supplier>> listSkills, + Function> readSkill) { + return new SkillProvider() { + @Override + public CompletableFuture> listSkills() { + return listSkills.get(); + } + + @Override + public CompletableFuture readSkill(String name) { + return readSkill.apply(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 774ae10164..3fc2b499d1 100644 --- a/java/sdk/src/test/java/com/github/copilot/RpcWrappersTest.java +++ b/java/sdk/src/test/java/com/github/copilot/RpcWrappersTest.java @@ -49,7 +49,9 @@ import com.github.copilot.generated.rpc.SessionConnectorsReconnectParams; 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.SessionModelSwitchToParams; +import com.github.copilot.generated.rpc.SessionModelSwitchToRequest; import com.github.copilot.generated.rpc.SessionRpc; /** @@ -188,6 +190,33 @@ void serverRpc_mcp_discover_passes_params() { // ── SessionRpc tests ────────────────────────────────────────────────────── + @Test + @AllowCopilotExperimental + void sessionRpc_mcp_listConfigured_preserves_runtime_lifecycle() throws Exception { + var stub = new StubCaller(); + stub.nextResult = new ObjectMapper().readValue(""" + { + "servers": [ + {"name":"cold","enabled":true,"live":{"status":"not_configured"}}, + {"name":"old-connection","enabled":false,"live":{"status":"connected"}}, + {"name":"auth","enabled":true,"live":{"status":"needs-auth"}}, + {"name":"stopped","enabled":true,"live":{"status":"stopped"}}, + {"name":"failed","enabled":true,"live":{"status":"failed","error":"connection failed"}} + ] + } + """, SessionMcpListConfiguredResult.class); + var session = new SessionRpc(stub, "session-mcp"); + + var inventory = session.mcp.listConfigured().get(); + + assertEquals(List.of("not_configured", "connected", "needs-auth", "stopped", "failed"), + inventory.servers().stream().map(server -> server.live().status().getValue()).toList()); + assertFalse(inventory.servers().get(1).enabled()); + assertEquals("connection failed", inventory.servers().get(4).live().error()); + assertEquals(List.of(new StubCaller.Call("session.mcp.listConfigured", Map.of("sessionId", "session-mcp"), + SessionMcpListConfiguredResult.class)), stub.calls); + } + @Test void sessionRpc_instantiates_with_all_namespace_fields() { var stub = new StubCaller(); @@ -445,8 +474,9 @@ void sessionRpc_model_switchTo_merges_sessionId_with_extra_params() { var switchParams = new SessionModelSwitchToParams(null, "gpt-5", null, null, null, null, null, null, null, null, null, null, null, null, null, null); session.model.switchTo(switchParams); + session.model.switchTo(new SessionModelSwitchToRequest("gpt-5").setProviderId("catalog-provider")); - assertEquals(1, stub.calls.size()); + assertEquals(2, stub.calls.size()); assertEquals("session.model.switchTo", stub.calls.get(0).method()); // Params should be a JsonNode containing both sessionId and modelId @@ -454,7 +484,12 @@ void sessionRpc_model_switchTo_merges_sessionId_with_extra_params() { assertInstanceOf(com.fasterxml.jackson.databind.node.ObjectNode.class, params); var node = (com.fasterxml.jackson.databind.node.ObjectNode) params; assertEquals("sess-xyz", node.get("sessionId").asText()); + assertFalse(node.has("providerId")); assertEquals("gpt-5", node.get("modelId").asText()); + var providerRequest = (com.fasterxml.jackson.databind.node.ObjectNode) stub.calls.get(1).params(); + assertEquals("sess-xyz", providerRequest.get("sessionId").asText()); + assertEquals("catalog-provider", providerRequest.get("providerId").asText()); + assertEquals("gpt-5", providerRequest.get("modelId").asText()); } @Test diff --git a/java/sdk/src/test/java/com/github/copilot/RuntimeHostIT.java b/java/sdk/src/test/java/com/github/copilot/RuntimeHostIT.java index 43b286da48..ab00b483c6 100644 --- a/java/sdk/src/test/java/com/github/copilot/RuntimeHostIT.java +++ b/java/sdk/src/test/java/com/github/copilot/RuntimeHostIT.java @@ -46,6 +46,8 @@ private static final class Application { final E2ETestContext context; final CopilotClient client; volatile CopilotSession session; + volatile String createSessionId; + volatile String resumeSessionId; final AtomicInteger creates = new AtomicInteger(); final AtomicInteger resumes = new AtomicInteger(); final AtomicInteger toolCalls = new AtomicInteger(); @@ -99,14 +101,24 @@ AhpHostOptions options() { creates.incrementAndGet(); assertFalse(request.cancellation().toCompletableFuture().isDone()); assertEquals(context.getWorkDir().toString(), request.config().getWorkingDirectory()); - return client.createSession(configure(request.config())).thenApply(value -> session = value); + createSessionId = request.config().getSessionId(); + assertNotNull(createSessionId); + assertFalse(createSessionId.isEmpty()); + return client.createSession(configure(request.config())).thenApply(value -> { + assertEquals(createSessionId, value.getSessionId()); + return session = value; + }); }).setResumeSession(request -> { resumes.incrementAndGet(); assertFalse(request.cancellation().toCompletableFuture().isDone()); assertEquals(false, request.config().getContinuePendingWork().orElseThrow()); assertEquals(context.getWorkDir().toString(), request.config().getWorkingDirectory()); + resumeSessionId = request.sessionId(); return client.resumeSession(request.sessionId(), configure(request.config())) - .thenApply(value -> session = value); + .thenApply(value -> { + assertEquals(resumeSessionId, value.getSessionId()); + return session = value; + }); }).setOnSessionReleased(value -> { releases.add(value); released.complete(null); @@ -174,13 +186,14 @@ void createsOrPublishesExactApplicationSession(boolean publish) throws Exception } else { sessionId = ahp.request(Map.of("op", "create", "clientId", clientId, "workDir", context.getWorkDir().toString())).get("sessionId").asText(); - assertEquals(sessionId, app.session.getSessionId()); + assertEquals(app.createSessionId, app.session.getSessionId()); + assertNotEquals(sessionId, app.session.getSessionId()); assertEquals(1, app.creates.get()); } var response = ahp.request( Map.of("op", "turn", "clientId", clientId, "sessionId", sessionId, "prompt", TOOL_PROMPT)); assertTrue(response.get("text").asText().contains("MAGIC_hello_42")); - app.assertCallbacks(sessionId); + app.assertCallbacks(app.session.getSessionId()); await(CompletableFuture.allOf(host.dispose(), host.dispose())); stopped(ahp, host, clientId); if (publish) { @@ -213,6 +226,9 @@ void resumesAfterRuntimeRestartAndComposesTools() throws Exception { sessionId = ahp.request(Map.of("op", "create", "clientId", clientId, "workDir", context.getWorkDir().toString(), "clientTools", true)).get("sessionId").asText(); original = app.session; + assertEquals(1, app.creates.get()); + assertEquals(app.createSessionId, original.getSessionId()); + assertNotEquals(sessionId, original.getSessionId()); assertTrue(ahp.request(Map.of("op", "turn", "clientId", clientId, "sessionId", sessionId, "prompt", "What is 2+2?")).get("text").asText().contains("4")); await(host.dispose()); @@ -232,14 +248,15 @@ void resumesAfterRuntimeRestartAndComposesTools() throws Exception { assertEquals("What is 2+2?", attached.get("history").get(0).get("message").get("text").asText()); assertEquals(0, app.creates.get()); assertEquals(1, app.resumes.get()); - assertEquals(sessionId, app.session.getSessionId()); + assertEquals(original.getSessionId(), app.resumeSessionId); + assertEquals(original.getSessionId(), app.session.getSessionId()); assertNotSame(original, app.session); var response = ahp.request(Map.of("op", "turn", "clientId", clientId, "sessionId", sessionId, "prompt", COMPOSED_PROMPT, "clientTools", true)); assertTrue(response.get("text").asText().contains("MAGIC_hello_42")); assertTrue(response.get("text").asText().contains("CLIENT_ECHO_ping")); assertEquals(1, response.get("clientToolCalls").asInt()); - app.assertCallbacks(sessionId); + app.assertCallbacks(original.getSessionId()); await(host.dispose()); stopped(ahp, host, clientId); app.assertReleased(); diff --git a/java/sdk/src/test/java/com/github/copilot/SessionAutoTierEventTest.java b/java/sdk/src/test/java/com/github/copilot/SessionAutoTierEventTest.java index 4378c105cd..ef7034c455 100644 --- a/java/sdk/src/test/java/com/github/copilot/SessionAutoTierEventTest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionAutoTierEventTest.java @@ -30,11 +30,11 @@ class SessionAutoTierEventTest { private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); @ParameterizedTest - @CsvSource({"session.start,EFFICIENCY,efficiency", "session.start,BALANCE,balance", - "session.start,INTELLIGENCE,intelligence", "session.start,FAST,fast", - "session.resume,EFFICIENCY,efficiency", "session.resume,BALANCE,balance", - "session.resume,INTELLIGENCE,intelligence", "session.resume,FAST,fast"}) - void canonicalAutoTierRoundTrips(String type, AutoTier tier, String value) throws Exception { + @CsvSource({"session.start,efficiency", "session.start,balance", "session.start,intelligence", "session.start,fast", + "session.start,premium-v2", "session.resume,efficiency", "session.resume,balance", + "session.resume,intelligence", "session.resume,fast", "session.resume,premium-v2"}) + void canonicalAutoTierRoundTrips(String type, String value) throws Exception { + AutoTier tier = AutoTier.fromValue(value); String json = """ {"type":"%s","data":{"selectedModel":"auto","autoTier":"%s"}} """.formatted(type, value); diff --git a/java/sdk/src/test/java/com/github/copilot/SessionAutoTierSwitchTest.java b/java/sdk/src/test/java/com/github/copilot/SessionAutoTierSwitchTest.java index a1c5dd2f44..96b9bf0319 100644 --- a/java/sdk/src/test/java/com/github/copilot/SessionAutoTierSwitchTest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionAutoTierSwitchTest.java @@ -33,6 +33,45 @@ @AllowCopilotExperimental class SessionAutoTierSwitchTest { + @Test + void customTierRoundTripsAcrossConvenienceAndGeneratedApis() throws Exception { + ObjectMapper mapper = JsonRpcClient.getObjectMapper(); + AutoTier tier = AutoTier.fromValue("premium-v2"); + assertEquals("\"premium-v2\"", mapper.writeValueAsString(tier)); + assertEquals(tier, mapper.readValue("\"premium-v2\"", AutoTier.class)); + var generated = mapper.readValue("\"premium-v2\"", com.github.copilot.generated.rpc.AutoTier.class); + assertEquals("premium-v2", generated.getValue()); + assertEquals("\"premium-v2\"", mapper.writeValueAsString(generated)); + assertEquals(AutoTier.BALANCE, AutoTier.fromValue("balance")); + } + + @Test + void legacy_model_switch_preserves_reasoning_and_omits_provider() throws Exception { + try (var sockets = new SocketPair()) { + var session = new CopilotSession("sess-legacy", sockets.client()); + session.setModel("gpt-5", "high"); + var params = sockets.stubServer().readOneMessage().get("params"); + assertEquals("gpt-5", params.get("modelId").asText()); + assertEquals("high", params.get("reasoningEffort").asText()); + assertFalse(params.has("providerId")); + assertFalse(params.has("autoTier")); + } + } + + @Test + void model_switch_with_summary_preserves_its_wire_fields() throws Exception { + try (var sockets = new SocketPair()) { + var session = new CopilotSession("sess-summary", sockets.client()); + session.setModel("gpt-5", "high", "concise", null); + var params = sockets.stubServer().readOneMessage().get("params"); + assertEquals("gpt-5", params.get("modelId").asText()); + assertEquals("high", params.get("reasoningEffort").asText()); + assertEquals("concise", params.get("reasoningSummary").asText()); + assertFalse(params.has("providerId")); + assertFalse(params.has("autoTier")); + } + } + @Test void setModel_omits_autoTier_when_no_preference_is_requested() 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 d8bc68906b..8f2edf52e0 100644 --- a/java/sdk/src/test/java/com/github/copilot/SessionEventHandlingTest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionEventHandlingTest.java @@ -35,6 +35,7 @@ import com.github.copilot.generated.SessionIdleEvent; import com.github.copilot.generated.SessionErrorEvent; import com.github.copilot.generated.SessionMode; +import com.github.copilot.generated.SessionResumeEvent; import com.github.copilot.generated.SessionStartEvent; import com.github.copilot.generated.rpc.SessionToolsGetCurrentMetadataResult; import com.github.copilot.rpc.MessageOptions; @@ -230,6 +231,24 @@ void testTypedEventHandler() { assertEquals("Second message", receivedMessages.get(1).getData().content()); } + @Test + void testSendReturnsAfterRpcAcceptanceWithoutWaitingForIdle() throws Exception { + var rpc = mock(JsonRpcClient.class); + when(rpc.invoke(eq("session.send"), any(), eq(SendMessageResponse.class))) + .thenReturn(CompletableFuture.completedFuture(new SendMessageResponse("message-1"))); + session = createTestSession(rpc); + var events = new ArrayList(); + session.on(events::add); + + session.send("hello").get(1, TimeUnit.SECONDS); + + verify(rpc).invoke(eq("session.send"), any(), eq(SendMessageResponse.class)); + assertTrue(events.isEmpty()); + dispatchEvent(createAssistantMessageEvent("done")); + dispatchEvent(createSessionIdleEvent()); + assertEquals(List.of("assistant.message", "session.idle"), events.stream().map(SessionEvent::getType).toList()); + } + @Test void testSendAndWaitSkipsAutopilotContinuationIdle() throws Exception { var rpc = mock(JsonRpcClient.class); @@ -385,9 +404,7 @@ void testHandlerReceivesCorrectEventData() { capturedSessionId.set(start.getData().sessionId()); }); - SessionStartEvent startEvent = createSessionStartEvent(); - startEvent.setData(new SessionStartEvent.SessionStartEventData("my-session-123", null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null)); + SessionStartEvent startEvent = createSessionStartEvent("my-session-123"); dispatchEvent(startEvent); AssistantMessageEvent msgEvent = createAssistantMessageEvent("Test content"); @@ -397,6 +414,17 @@ void testHandlerReceivesCorrectEventData() { assertEquals("Test content", capturedContent.get()); } + @Test + void testLegacyLifecycleEventConstructorsRemainAvailable() { + var start = new SessionStartEvent.SessionStartEventData("session", null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null); + var resume = new SessionResumeEvent.SessionResumeEventData(null, 0L, null, null, null, null, null, null, null, + null, null, null, null, null, null, null); + + assertEquals("session", start.sessionId()); + assertEquals(0L, resume.eventCount()); + } + @Test void testHandlerExceptionDoesNotBreakOtherHandlers() { var handler2Events = new ArrayList(); @@ -1064,16 +1092,16 @@ private SessionStartEvent createSessionStartEvent() { private SessionStartEvent createSessionStartEvent(String sessionId) { var event = new SessionStartEvent(); var data = new SessionStartEvent.SessionStartEventData(sessionId, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null); + null, null, null, null, null, null, null, null, null, null); event.setData(data); return event; } private AssistantMessageEvent createAssistantMessageEvent(String content) { var event = new AssistantMessageEvent(); - var data = new AssistantMessageEvent.AssistantMessageEventData(null, null, null, content, null, null, null, + var data = new AssistantMessageEvent.AssistantMessageEventData(null, null, null, null, content, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null); + null, null); event.setData(data); return event; } diff --git a/java/sdk/src/test/java/com/github/copilot/SessionEventsE2ETest.java b/java/sdk/src/test/java/com/github/copilot/SessionEventsE2ETest.java index 8c8fc154aa..40a23c5cf3 100644 --- a/java/sdk/src/test/java/com/github/copilot/SessionEventsE2ETest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionEventsE2ETest.java @@ -11,7 +11,7 @@ import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -67,7 +67,7 @@ void testShouldReceiveSessionEvents_assistantTurnEvents() throws Exception { // Use existing session snapshot that emits turn events ctx.configureForTest("session", "should_receive_session_events"); - var allEvents = new ArrayList(); + var allEvents = new CopyOnWriteArrayList(); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client @@ -109,7 +109,7 @@ void testShouldReceiveSessionEvents_userMessageEvent() throws Exception { // Use existing session snapshot ctx.configureForTest("session", "should_receive_session_events"); - var userMessages = new ArrayList(); + var userMessages = new CopyOnWriteArrayList(); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client @@ -135,8 +135,8 @@ void testInvokesBuiltInTools_toolExecutionCompleteEvent() throws Exception { // Use existing tools snapshot for built-in tool invocation ctx.configureForTest("tools", "invokes_built_in_tools"); - var toolStarts = new ArrayList(); - var toolCompletes = new ArrayList(); + var toolStarts = new CopyOnWriteArrayList(); + var toolCompletes = new CopyOnWriteArrayList(); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client @@ -175,7 +175,7 @@ void testShouldReceiveSessionEvents_assistantUsageEvent() throws Exception { // Use existing session snapshot ctx.configureForTest("session", "should_receive_session_events"); - var usageEvents = new ArrayList(); + var usageEvents = new CopyOnWriteArrayList(); try (CopilotClient client = ctx.createClient()) { CopilotSession session = client @@ -206,7 +206,7 @@ void testShouldReceiveSessionEvents_sessionIdleAfterMessage() throws Exception { // Use existing session snapshot ctx.configureForTest("session", "should_receive_session_events"); - var allEvents = new ArrayList(); + var allEvents = new CopyOnWriteArrayList(); var idleEntered = new CountDownLatch(1); var releaseIdle = new CountDownLatch(1); var idleCallbackCompleted = new AtomicBoolean(); @@ -274,7 +274,7 @@ void testInvokesBuiltInTools_eventOrderDuringToolExecution() throws Exception { // Use existing tools snapshot for built-in tool invocation ctx.configureForTest("tools", "invokes_built_in_tools"); - var eventTypes = new ArrayList(); + var eventTypes = new CopyOnWriteArrayList(); // Track this listener's receipt of session.idle explicitly. var idleReceived = new java.util.concurrent.CompletableFuture(); 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 345fdccff9..001489e41f 100644 --- a/java/sdk/src/test/java/com/github/copilot/SessionHandlerTest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionHandlerTest.java @@ -6,10 +6,15 @@ import static org.junit.jupiter.api.Assertions.*; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -22,6 +27,8 @@ import com.github.copilot.rpc.SessionEndHookOutput; import com.github.copilot.rpc.SessionHooks; import com.github.copilot.rpc.SessionStartHookOutput; +import com.github.copilot.rpc.SubagentStartHookOutput; +import com.github.copilot.rpc.SubagentStopHookOutput; import com.github.copilot.rpc.ToolDefinition; import com.github.copilot.rpc.UserInputRequest; import com.github.copilot.rpc.UserInputResponse; @@ -310,6 +317,81 @@ void testHandleHooksInvokeAgentStop() throws Exception { assertEquals("finish the remaining work", output.getReason()); } + @Test + void testHandleHooksInvokeSubagentStart() throws Exception { + var hooks = new SessionHooks().setOnSubagentStart((hookInput, invocation) -> { + assertEquals("handler-test-session", invocation.getSessionId()); + assertEquals("parent-session", hookInput.sessionId()); + assertEquals(1735689600000L, hookInput.timestamp()); + assertEquals("/tmp", hookInput.cwd()); + assertEquals("/tmp/transcript.jsonl", hookInput.transcriptPath()); + assertEquals("explore", hookInput.agentName()); + assertEquals("Explore Agent", hookInput.agentDisplayName()); + assertEquals("Read code", hookInput.agentDescription()); + return CompletableFuture.completedFuture(new SubagentStartHookOutput("Follow the file")); + }); + assertTrue(hooks.hasHooks()); + session.registerHooks(hooks); + + JsonNode input = MAPPER.valueToTree(Map.of("sessionId", "parent-session", "timestamp", 1735689600000L, "cwd", + "/tmp", "transcriptPath", "/tmp/transcript.jsonl", "agentName", "explore", "agentDisplayName", + "Explore Agent", "agentDescription", "Read code")); + + var output = assertInstanceOf(SubagentStartHookOutput.class, + session.handleHooksInvoke("subagentStart", input).get()); + assertEquals("Follow the file", MAPPER.valueToTree(output).get("additionalContext").asText()); + } + + @Test + void testHandleHooksInvokeSubagentStop() throws Exception { + var hooks = new SessionHooks().setOnSubagentStop((hookInput, invocation) -> { + assertEquals("handler-test-session", invocation.getSessionId()); + assertEquals("parent-session", hookInput.sessionId()); + assertEquals(1735689600001L, hookInput.timestamp()); + assertEquals("/tmp", hookInput.cwd()); + assertEquals("/tmp/transcript.jsonl", hookInput.transcriptPath()); + assertEquals("explore", hookInput.agentName()); + assertEquals("explore", hookInput.agentType()); + assertEquals("read-file", hookInput.agentId()); + assertEquals("Explore Agent", hookInput.agentDisplayName()); + assertEquals("Read code", hookInput.agentDescription()); + assertEquals("end_turn", hookInput.stopReason()); + assertEquals("original answer", hookInput.response()); + return CompletableFuture.completedFuture(new SubagentStopHookOutput(null, null, "rewritten answer")); + }); + assertTrue(hooks.hasHooks()); + session.registerHooks(hooks); + + JsonNode input = MAPPER.valueToTree(Map.ofEntries(Map.entry("sessionId", "parent-session"), + Map.entry("timestamp", 1735689600001L), Map.entry("cwd", "/tmp"), + Map.entry("transcriptPath", "/tmp/transcript.jsonl"), Map.entry("agentName", "explore"), + Map.entry("agentType", "explore"), Map.entry("agentId", "read-file"), + Map.entry("agentDisplayName", "Explore Agent"), Map.entry("agentDescription", "Read code"), + Map.entry("stopReason", "end_turn"), Map.entry("response", "original answer"))); + + var output = assertInstanceOf(SubagentStopHookOutput.class, + session.handleHooksInvoke("subagentStop", input).get()); + JsonNode result = MAPPER.valueToTree(output); + assertEquals("rewritten answer", result.get("modifiedResponse").asText()); + assertFalse(result.has("decision")); + assertFalse(result.has("reason")); + } + + @Test + void testHandleHooksInvokeSubagentStopBlock() throws Exception { + session.registerHooks(new SessionHooks().setOnSubagentStop((input, invocation) -> CompletableFuture + .completedFuture(new SubagentStopHookOutput("block", "Keep researching", null)))); + + JsonNode result = MAPPER + .valueToTree(session + .handleHooksInvoke("subagentStop", + MAPPER.valueToTree(Map.of("sessionId", "parent-session", "agentName", "explore"))) + .get()); + assertEquals("block", result.get("decision").asText()); + assertEquals("Keep researching", result.get("reason").asText()); + assertFalse(result.has("modifiedResponse")); + } + // ===== handleHooksInvoke: sessionId deserialization on hook inputs ===== @Test @@ -371,6 +453,44 @@ void testHandleHooksInvokeUnhandledHookType() throws Exception { assertNull(result); } + @Test + void testHandleHooksInvokeSubagentLifecycleDoesNotLogAsUnhandled() throws Exception { + session.registerHooks(new SessionHooks()); + Logger logger = Logger.getLogger(CopilotSession.class.getName()); + Level previousLevel = logger.getLevel(); + List unhandled = new ArrayList<>(); + Handler handler = new Handler() { + @Override + public void publish(LogRecord record) { + if (record.getMessage().startsWith("Unhandled hook type: ")) { + unhandled.add(record.getMessage()); + } + } + + @Override + public void flush() { + } + + @Override + public void close() { + } + }; + handler.setLevel(Level.FINE); + logger.addHandler(handler); + logger.setLevel(Level.FINE); + try { + JsonNode input = MAPPER.valueToTree(Map.of("sessionId", "handler-test-session", "agentName", "task")); + assertNull(session.handleHooksInvoke("subagentStart", input).get()); + assertNull(session.handleHooksInvoke("subagentStop", input).get()); + assertNull(session.handleHooksInvoke("unknownHookType", input).get()); + + assertEquals(List.of("Unhandled hook type: unknownHookType"), unhandled); + } finally { + logger.removeHandler(handler); + logger.setLevel(previousLevel); + } + } + // ===== handleHooksInvoke: handler throws ===== @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 c7cb1be6bc..978346e486 100644 --- a/java/sdk/src/test/java/com/github/copilot/SessionRequestBuilderTest.java +++ b/java/sdk/src/test/java/com/github/copilot/SessionRequestBuilderTest.java @@ -111,6 +111,7 @@ void testBuildCreateRequestNullConfig() { assertNull(request.getModel()); assertTrue(request.getRequestPermission(), "requestPermission should be true even for null config"); assertEquals("direct", request.getEnvValueMode(), "envValueMode should be 'direct' even for null config"); + assertNull(request.getHasSkillProvider()); } @Test @@ -162,6 +163,36 @@ void askUserVariantDefaultsToOmittedLegacyBehavior() throws Exception { assertFalse(mapper.readTree(mapper.writeValueAsBytes(resumeRequest)).has("askUserVariant")); } + @Test + @AllowCopilotExperimental + void skillProviderFlagIsForwardedAndSerializedForCreateAndResume() throws Exception { + SkillProvider provider = testSkillProvider(); + var mapper = JsonRpcClient.getObjectMapper(); + + var createRequest = SessionRequestBuilder.buildCreateRequest(new SessionConfig().setSkillProvider(provider), + "skill-provider-create"); + var resumeRequest = SessionRequestBuilder.buildResumeRequest("skill-provider-resume", + new ResumeSessionConfig().setSkillProvider(provider)); + + assertTrue(createRequest.getHasSkillProvider()); + assertTrue(resumeRequest.getHasSkillProvider()); + assertTrue(mapper.readTree(mapper.writeValueAsBytes(createRequest)).path("hasSkillProvider").asBoolean()); + assertTrue(mapper.readTree(mapper.writeValueAsBytes(resumeRequest)).path("hasSkillProvider").asBoolean()); + } + + @Test + void skillProviderFlagIsOmittedWhenUnset() throws Exception { + var mapper = JsonRpcClient.getObjectMapper(); + var createRequest = SessionRequestBuilder.buildCreateRequest(new SessionConfig(), "skill-provider-create"); + var resumeRequest = SessionRequestBuilder.buildResumeRequest("skill-provider-resume", + new ResumeSessionConfig()); + + assertNull(createRequest.getHasSkillProvider()); + assertNull(resumeRequest.getHasSkillProvider()); + assertFalse(mapper.readTree(mapper.writeValueAsBytes(createRequest)).has("hasSkillProvider")); + assertFalse(mapper.readTree(mapper.writeValueAsBytes(resumeRequest)).has("hasSkillProvider")); + } + @Test void diagnosticsAreForwardedAndOmittedForCreateAndColdResume() throws Exception { var mapper = JsonRpcClient.getObjectMapper(); @@ -399,6 +430,7 @@ void testBuildResumeRequestNullConfig() { assertNull(request.getContinuePendingWork()); assertTrue(request.getRequestPermission(), "requestPermission should be true even for null config"); assertEquals("direct", request.getEnvValueMode(), "envValueMode should be 'direct' even for null config"); + assertNull(request.getHasSkillProvider()); } @Test @@ -850,6 +882,17 @@ void configureSessionWithNullConfig_returnsEarly() { assertDoesNotThrow(() -> SessionRequestBuilder.configureSession(session, (SessionConfig) null)); } + @Test + @AllowCopilotExperimental + void configureSessionWithSkillProvider_registersProvider() { + CopilotSession session = new CopilotSession("session-1", null); + SkillProvider provider = testSkillProvider(); + + SessionRequestBuilder.configureSession(session, new SessionConfig().setSkillProvider(provider)); + + assertSame(provider, session.getSkillProvider()); + } + @Test void configureSessionWithCommands_registersCommands() { CopilotSession session = new CopilotSession("session-1", null); @@ -922,6 +965,17 @@ void configureResumedSessionWithOnEvent_registersEventHandler() { SessionRequestBuilder.configureSession(session, config); } + @Test + @AllowCopilotExperimental + void configureResumedSessionWithSkillProvider_registersProvider() { + CopilotSession session = new CopilotSession("session-1", null); + SkillProvider provider = testSkillProvider(); + + SessionRequestBuilder.configureSession(session, new ResumeSessionConfig().setSkillProvider(provider)); + + assertSame(provider, session.getSkillProvider()); + } + @Test void testBuildCreateRequestWithDefaultAgent() { var defaultAgent = new DefaultAgentConfig().setExcludedTools(List.of("secret_tool")); @@ -1300,4 +1354,19 @@ void githubMcpToolConfigIsMappedAndSerializedForCreateAndResume() throws Excepti mapper.writeValueAsString(SessionRequestBuilder.buildCreateRequest(new SessionConfig(), "session-2")) .contains("\"githubMcpToolConfig\"")); } + + @AllowCopilotExperimental + private static SkillProvider testSkillProvider() { + return new SkillProvider() { + @Override + public CompletableFuture> listSkills() { + return CompletableFuture.completedFuture(List.of()); + } + + @Override + public CompletableFuture readSkill(String name) { + return CompletableFuture.completedFuture(null); + } + }; + } } diff --git a/java/sdk/src/test/java/com/github/copilot/SetToolsIT.java b/java/sdk/src/test/java/com/github/copilot/SetToolsIT.java new file mode 100644 index 0000000000..e68a6b30f0 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/SetToolsIT.java @@ -0,0 +1,243 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.github.copilot.generated.AssistantMessageEvent; +import com.github.copilot.rpc.MessageOptions; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.ResumeSessionConfig; +import com.github.copilot.rpc.SessionConfig; +import com.github.copilot.rpc.ToolDefinition; + +@AllowCopilotExperimental +class SetToolsIT { + + private static final String FRUIT_PROMPT = "Use lookup_fruit to find the fruit for code 42."; + private static final String FRUIT_AND_VEGETABLE_PROMPT = "Use lookup_fruit to find the fruit for code 42 again, and use lookup_vegetable to find the vegetable for code 7."; + private static final String VEGETABLE_PROMPT = "Use lookup_vegetable to find the vegetable for code 7."; + + private static E2ETestContext ctx; + + @BeforeAll + static void setup() throws Exception { + ctx = E2ETestContext.create(); + } + + @AfterAll + static void teardown() throws Exception { + if (ctx != null) { + ctx.close(); + } + } + + @Test + void replacesToolsOnACreatedSession() throws Exception { + ctx.configureForTest("set_tools", "replaces_tools_on_a_created_session"); + + var originalLookups = new CopyOnWriteArrayList(); + var replacementLookups = new CopyOnWriteArrayList(); + var vegetableLookups = new CopyOnWriteArrayList(); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client.createSession( + new SessionConfig().setTools(List.of(lookupFruit("apple", originalLookups), retiredLookup())) + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)) + .get(30, TimeUnit.SECONDS); + try { + AssistantMessageEvent first = session.sendAndWait(new MessageOptions().setPrompt(FRUIT_PROMPT)).get(90, + TimeUnit.SECONDS); + assertNotNull(first); + assertTrue(first.getData().content().contains("apple"), first.getData().content()); + + session.setTools( + List.of(lookupFruit("dragonfruit", replacementLookups), lookupVegetable(vegetableLookups))) + .get(30, TimeUnit.SECONDS); + + AssistantMessageEvent second = session + .sendAndWait(new MessageOptions().setPrompt(FRUIT_AND_VEGETABLE_PROMPT)) + .get(90, TimeUnit.SECONDS); + assertNotNull(second); + assertTrue(second.getData().content().contains("dragonfruit"), second.getData().content()); + assertTrue(second.getData().content().contains("carrot"), second.getData().content()); + assertEquals(List.of(42), originalLookups); + assertEquals(List.of(42), replacementLookups); + assertEquals(List.of(7), vegetableLookups); + + // Model requests after the replacement offer exactly the new tool set. + List> exchanges = ctx.getExchanges(); + int replacedFrom = indexOfPrompt(exchanges, FRUIT_AND_VEGETABLE_PROMPT); + assertTrue(replacedFrom > 0, "Expected model requests before and after the replacement"); + assertOffered(exchanges.subList(0, replacedFrom), List.of("lookup_fruit", "retired_lookup"), + List.of("lookup_vegetable")); + assertOffered(exchanges.subList(replacedFrom, exchanges.size()), + List.of("lookup_fruit", "lookup_vegetable"), List.of("retired_lookup")); + } finally { + session.close(); + } + } + } + + @Test + void replacesToolsOnAResumedSession() throws Exception { + ctx.configureForTest("set_tools", "replaces_tools_on_a_resumed_session"); + + try (CopilotClient client = ctx.createClient()) { + var createdLookups = new CopyOnWriteArrayList(); + CopilotSession created = client + .createSession(new SessionConfig().setTools(List.of(lookupFruit("apple", createdLookups))) + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)) + .get(30, TimeUnit.SECONDS); + String sessionId = created.getSessionId(); + AssistantMessageEvent first = created.sendAndWait(new MessageOptions().setPrompt(FRUIT_PROMPT)).get(90, + TimeUnit.SECONDS); + assertNotNull(first); + assertTrue(first.getData().content().contains("apple"), first.getData().content()); + assertEquals(List.of(42), createdLookups); + created.close(); + + var fruitLookups = new CopyOnWriteArrayList(); + var vegetableLookups = new CopyOnWriteArrayList(); + CopilotSession resumed = client.resumeSession(sessionId, + new ResumeSessionConfig().setTools(List.of(lookupFruit("apple", fruitLookups))) + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)) + .get(30, TimeUnit.SECONDS); + try { + resumed.setTools(List.of(lookupVegetable(vegetableLookups))).get(30, TimeUnit.SECONDS); + + AssistantMessageEvent answer = resumed.sendAndWait(new MessageOptions().setPrompt(VEGETABLE_PROMPT)) + .get(90, TimeUnit.SECONDS); + assertNotNull(answer); + assertTrue(answer.getData().content().contains("carrot"), answer.getData().content()); + assertEquals(List.of(7), vegetableLookups); + assertEquals(List.of(), fruitLookups); + + List> exchanges = ctx.getExchanges(); + int replacedFrom = indexOfPrompt(exchanges, VEGETABLE_PROMPT); + assertTrue(replacedFrom > 0, "Expected model requests before and after the replacement"); + assertOffered(exchanges.subList(replacedFrom, exchanges.size()), List.of("lookup_vegetable"), + List.of("lookup_fruit")); + } finally { + resumed.close(); + } + } + } + + @Test + void keepsThePreviousToolsWhenAReplacementIsRejected() throws Exception { + ctx.configureForTest("set_tools", "keeps_the_previous_tools_when_a_replacement_is_rejected"); + + var originalLookups = new CopyOnWriteArrayList(); + var replacementLookups = new CopyOnWriteArrayList(); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client + .createSession(new SessionConfig().setTools(List.of(lookupFruit("apple", originalLookups))) + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)) + .get(30, TimeUnit.SECONDS); + try { + assertThrows(ExecutionException.class, + () -> session.setTools(List.of(lookupFruit("dragonfruit", replacementLookups), invalidTool())) + .get(30, TimeUnit.SECONDS)); + + AssistantMessageEvent answer = session.sendAndWait(new MessageOptions().setPrompt(FRUIT_PROMPT)).get(90, + TimeUnit.SECONDS); + assertNotNull(answer); + assertTrue(answer.getData().content().contains("apple"), answer.getData().content()); + assertEquals(List.of(42), originalLookups); + assertEquals(List.of(), replacementLookups); + } finally { + session.close(); + } + } + } + + private static ToolDefinition lookupFruit(String fruit, List calls) { + return ToolDefinition.create("lookup_fruit", "Looks up the fruit for a numeric code", codeSchema("Fruit code"), + invocation -> { + calls.add(((Number) invocation.getArguments().get("code")).intValue()); + return CompletableFuture.completedFuture(fruit); + }); + } + + private static ToolDefinition lookupVegetable(List calls) { + return ToolDefinition.create("lookup_vegetable", "Looks up the vegetable for a numeric code", + codeSchema("Vegetable code"), invocation -> { + calls.add(((Number) invocation.getArguments().get("code")).intValue()); + return CompletableFuture.completedFuture("carrot"); + }); + } + + private static ToolDefinition retiredLookup() { + return ToolDefinition.create("retired_lookup", "Looks up a retired value", + Map.of("type", "object", "properties", Map.of()), + invocation -> CompletableFuture.completedFuture("retired")); + } + + private static ToolDefinition invalidTool() { + return ToolDefinition.create("invalid.tool", "Has a name the runtime rejects", + Map.of("type", "object", "properties", Map.of()), + invocation -> CompletableFuture.completedFuture("never")); + } + + private static Map codeSchema(String description) { + return Map.of("type", "object", "properties", + Map.of("code", Map.of("type", "integer", "description", description)), "required", List.of("code")); + } + + /** + * Returns the index of the first model request that carries {@code prompt} as a + * user message, or -1. + */ + private static int indexOfPrompt(List> exchanges, String prompt) { + for (int i = 0; i < exchanges.size(); i++) { + if (exchanges.get(i).get("request") instanceof Map request + && request.get("messages") instanceof List messages) { + for (Object message : messages) { + if (message instanceof Map fields && "user".equals(fields.get("role")) + && String.valueOf(fields.get("content")).contains(prompt)) { + return i; + } + } + } + } + return -1; + } + + /** + * Asserts that every model request offered the tools in {@code offered} and + * none of the tools in {@code notOffered}. + */ + private static void assertOffered(List> exchanges, List offered, + List notOffered) { + for (Map exchange : exchanges) { + var tools = new ArrayList(); + if (exchange.get("request") instanceof Map request && request.get("tools") instanceof List list) { + for (Object tool : list) { + if (tool instanceof Map fields && fields.get("function") instanceof Map function) { + tools.add(String.valueOf(function.get("name"))); + } + } + } + assertTrue(tools.containsAll(offered), "Offered " + tools + ", expected " + offered); + for (String name : notOffered) { + assertFalse(tools.contains(name), "Offered " + tools + ", expected no " + name); + } + } + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/SetToolsTest.java b/java/sdk/src/test/java/com/github/copilot/SetToolsTest.java new file mode 100644 index 0000000000..267d1375e4 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/SetToolsTest.java @@ -0,0 +1,242 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.copilot.generated.ExternalToolRequestedEvent; +import com.github.copilot.rpc.ToolDefinition; +import com.github.copilot.rpc.ToolDefer; + +@AllowCopilotExperimental +class SetToolsTest { + + private JsonRpcClient rpc; + private CopilotSession session; + + @BeforeEach + void setup() { + rpc = mock(JsonRpcClient.class); + session = new CopilotSession("set-tools-session", rpc); + } + + @Test + void sendsCompleteReplacementPayloadAndInstallsOnlyHandlersAfterAcceptance() throws Exception { + var accepted = new CompletableFuture(); + List requests = captureSetToolsRequests(accepted); + var originalCalls = new CopyOnWriteArrayList(); + var replacementCalls = new CopyOnWriteArrayList(); + session.registerTools(List.of(tool("original_tool", "Original", "old", originalCalls))); + + ToolDefinition declarationOnly = new ToolDefinition("declaration_only", null, Map.of("type", "object"), null, + true, true, ToolDefer.NEVER, Map.of("owner", "test"), true); + var pending = session + .setTools(List.of(tool("replacement_tool", null, "new", replacementCalls), declarationOnly)); + + assertEquals(1, requests.size()); + JsonNode payload = requests.get(0); + assertEquals("set-tools-session", payload.path("sessionId").asText()); + assertEquals("replacement_tool", payload.at("/tools/0/name").asText()); + assertEquals("", payload.at("/tools/0/description").asText()); + assertEquals("object", payload.at("/tools/0/parameters/type").asText()); + assertFalse(payload.at("/tools/0").has("handler")); + assertEquals("declaration_only", payload.at("/tools/1/name").asText()); + assertEquals("", payload.at("/tools/1/description").asText()); + assertTrue(payload.at("/tools/1/overridesBuiltInTool").asBoolean()); + assertTrue(payload.at("/tools/1/skipPermission").asBoolean()); + assertEquals("never", payload.at("/tools/1/defer").asText()); + assertTrue(payload.at("/tools/1/isTerminal").asBoolean()); + assertEquals("test", payload.at("/tools/1/metadata/owner").asText()); + + dispatchToolRequest("request-old", "original_tool"); + waitForCalls(originalCalls, List.of("original_tool")); + assertNull(session.getTool("replacement_tool")); + + accepted.complete(null); + pending.get(1, TimeUnit.SECONDS); + + assertNull(session.getTool("original_tool")); + dispatchToolRequest("request-new", "replacement_tool"); + waitForCalls(replacementCalls, List.of("replacement_tool")); + } + + @Test + void rejectionLeavesExistingHandlersUnchanged() throws Exception { + var rejected = new CompletableFuture(); + captureSetToolsRequests(rejected); + var originalCalls = new CopyOnWriteArrayList(); + var replacementCalls = new CopyOnWriteArrayList(); + session.registerTools(List.of(tool("lookup", "Original", "old", originalCalls))); + + var pending = session.setTools(List.of(tool("lookup", "Replacement", "new", replacementCalls))); + rejected.completeExceptionally(new IllegalArgumentException("invalid params")); + + var error = assertThrows(ExecutionException.class, () -> pending.get(1, TimeUnit.SECONDS)); + assertInstanceOf(IllegalArgumentException.class, error.getCause()); + dispatchToolRequest("request-after-reject", "lookup"); + waitForCalls(originalCalls, List.of("lookup")); + assertTrue(replacementCalls.isEmpty()); + } + + @Test + void emptyListRemovesHandlersAfterAcceptance() throws Exception { + var accepted = new CompletableFuture(); + List requests = captureSetToolsRequests(accepted); + session.registerTools(List.of(tool("lookup", "Original", "old", new CopyOnWriteArrayList<>()))); + + var pending = session.setTools(List.of()); + assertTrue(requests.get(0).path("tools").isArray()); + assertEquals(0, requests.get(0).path("tools").size()); + + accepted.complete(null); + pending.get(1, TimeUnit.SECONDS); + assertNull(session.getTool("lookup")); + } + + @Test + void concurrentCallsAreSentInOrderAndFailureDoesNotBlockLaterCall() throws Exception { + var rpcResults = List.of(new CompletableFuture(), new CompletableFuture()); + var nextResult = new AtomicInteger(); + var requests = new ArrayList(); + when(rpc.invoke(eq("session.tools.set"), any(), eq(Void.class))).thenAnswer(invocation -> { + requests.add(invocation.getArgument(1)); + return rpcResults.get(nextResult.getAndIncrement()); + }); + + var first = session.setTools(List.of(tool("first", "First", "first", new CopyOnWriteArrayList<>()))); + var second = session.setTools(List.of(tool("second", "Second", "second", new CopyOnWriteArrayList<>()))); + + assertEquals(1, requests.size(), "second replacement must wait for the first RPC to finish"); + assertEquals("first", requests.get(0).at("/tools/0/name").asText()); + + rpcResults.get(0).completeExceptionally(new IllegalArgumentException("rejected")); + var firstError = assertThrows(ExecutionException.class, () -> first.get(1, TimeUnit.SECONDS)); + assertInstanceOf(IllegalArgumentException.class, firstError.getCause()); + assertEquals(2, requests.size()); + assertEquals("second", requests.get(1).at("/tools/0/name").asText()); + + rpcResults.get(1).complete(null); + second.get(1, TimeUnit.SECONDS); + assertNull(session.getTool("first")); + assertNotNull(session.getTool("second")); + } + + @Test + void cancellingReturnedFutureDoesNotPreventAcceptedReplacementInstallingHandlers() throws Exception { + var accepted = new CompletableFuture(); + captureSetToolsRequests(accepted); + + var pending = session + .setTools(List.of(tool("replacement", "Replacement", "new", new CopyOnWriteArrayList<>()))); + assertTrue(pending.cancel(true)); + + accepted.complete(null); + + assertTrue(pending.isCancelled()); + assertNotNull(session.getTool("replacement")); + } + + @Test + void cancellingWhileQueuedSendsNothing() throws Exception { + var firstResult = new CompletableFuture(); + var requests = captureSetToolsRequests(firstResult); + + var first = session.setTools(List.of(tool("first", "First", "first", new CopyOnWriteArrayList<>()))); + var queued = session.setTools(List.of(tool("queued", "Queued", "queued", new CopyOnWriteArrayList<>()))); + assertTrue(queued.cancel(true)); + + firstResult.complete(null); + first.get(1, TimeUnit.SECONDS); + + assertEquals(1, requests.size()); + assertEquals("first", requests.get(0).at("/tools/0/name").asText()); + assertNotNull(session.getTool("first")); + assertNull(session.getTool("queued")); + } + + @Test + void closeRejectsLateAcknowledgementsAndQueuedReplacements() throws Exception { + var accepted = new CompletableFuture(); + var requests = captureSetToolsRequests(accepted); + when(rpc.invoke(eq("session.detach"), any(), eq(CopilotSession.SessionDetachResponse.class))) + .thenReturn(CompletableFuture.completedFuture(new CopilotSession.SessionDetachResponse(true, null))); + + var pending = session.setTools(List.of(tool("pending", "Pending", "first", new CopyOnWriteArrayList<>()))); + var queued = session.setTools(List.of(tool("queued", "Queued", "second", new CopyOnWriteArrayList<>()))); + assertEquals(1, requests.size(), "Only the admitted replacement may reach the RPC boundary"); + assertFalse(pending.isDone()); + assertFalse(queued.isDone()); + + session.close(); + assertTrue(accepted.complete(null), "Acknowledge the admitted replacement only after retirement"); + + var pendingError = assertThrows(ExecutionException.class, () -> pending.get(1, TimeUnit.SECONDS)); + var queuedError = assertThrows(ExecutionException.class, () -> queued.get(1, TimeUnit.SECONDS)); + assertInstanceOf(IllegalStateException.class, pendingError.getCause()); + assertInstanceOf(IllegalStateException.class, queuedError.getCause()); + assertEquals("Session is closed", pendingError.getCause().getMessage()); + assertEquals("Session is closed", queuedError.getCause().getMessage()); + assertEquals(1, requests.size(), "A replacement queued before retirement must not start afterwards"); + assertThrows(IllegalStateException.class, () -> session.setTools(List.of())); + } + + private List captureSetToolsRequests(CompletableFuture result) { + var requests = new ArrayList(); + when(rpc.invoke(eq("session.tools.set"), any(), eq(Void.class))).thenAnswer(invocation -> { + requests.add(invocation.getArgument(1)); + return result; + }); + return requests; + } + + private static ToolDefinition tool(String name, String description, String result, List calls) { + return ToolDefinition.create(name, description, Map.of("type", "object"), invocation -> { + calls.add(name); + return CompletableFuture.completedFuture(result); + }); + } + + private void dispatchToolRequest(String requestId, String toolName) { + var requested = new ExternalToolRequestedEvent(); + requested.setData(new ExternalToolRequestedEvent.ExternalToolRequestedEventData(requestId, "set-tools-session", + "tool-call-" + requestId, toolName, null, Map.of(), null, null, null)); + try { + Method dispatchMethod = CopilotSession.class.getDeclaredMethod("dispatchEvent", + com.github.copilot.generated.SessionEvent.class); + dispatchMethod.setAccessible(true); + dispatchMethod.invoke(session, requested); + } catch (Exception e) { + throw new RuntimeException("Failed to dispatch tool request", e); + } + } + + private static void waitForCalls(List calls, List expected) throws Exception { + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(1); + while (System.nanoTime() < deadline) { + if (calls.equals(expected)) { + return; + } + Thread.onSpinWait(); + } + assertEquals(expected, calls); + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/SkillProviderTest.java b/java/sdk/src/test/java/com/github/copilot/SkillProviderTest.java new file mode 100644 index 0000000000..7a4e6f3329 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/SkillProviderTest.java @@ -0,0 +1,421 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.*; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import com.github.copilot.generated.AssistantMessageEvent; +import com.github.copilot.generated.SessionEvent; +import com.github.copilot.generated.ToolExecutionCompleteEvent; +import com.github.copilot.generated.rpc.Skill; +import com.github.copilot.generated.rpc.SkillSource; +import com.github.copilot.rpc.CloudSessionOptions; +import com.github.copilot.rpc.MessageOptions; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.ResumeSessionConfig; +import com.github.copilot.rpc.SessionConfig; + +@AllowCopilotExperimental +class SkillProviderTest { + + private static E2ETestContext ctx; + + @BeforeAll + static void setup() throws Exception { + ctx = E2ETestContext.create(); + } + + @AfterAll + static void teardown() throws Exception { + if (ctx != null) { + ctx.close(); + } + } + + @Test + void should_load_provider_skill_lazily_through_skill_tool() throws Exception { + ctx.configureForTest("skill_provider", "should_load_provider_skill_lazily_through_skill_tool"); + + var provider = new TestSkillProvider( + List.of(skill("provider-lookup", "Reports the provider lookup verification word.", + "# Provider lookup\n\nThe verification word is TANGERINE_QUARTZ_19. Reply with it.\n"))); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client.createSession(new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(provider)) + .get(30, TimeUnit.SECONDS); + + try { + Skill listed = session.getRpc().skills.list().get(30, TimeUnit.SECONDS).skills().stream() + .filter(s -> "provider-lookup".equals(s.name())).findFirst().orElse(null); + assertNotNull(listed, "Expected provider-lookup to be listed"); + assertEquals(SkillSource.SDK, listed.source()); + assertTrue(listed.enabled()); + assertEquals("", listed.path() == null ? "" : listed.path()); + assertEquals(List.of(), provider.reads()); + + AssistantMessageEvent message = session.sendAndWait(new MessageOptions().setPrompt( + "Use the skill tool to load the provider-lookup skill, then reply with its verification word."), + 60_000).get(90, TimeUnit.SECONDS); + + assertEquals(List.of("provider-lookup"), provider.reads()); + // Validate the final assistant response arrived (guards against truncated + // captures) + assertTrue(assistantText(message).contains("TANGERINE_QUARTZ_19")); + } finally { + session.close(); + } + } + } + + @Test + void should_load_provider_and_file_based_skills_together() throws Exception { + ctx.configureForTest("skill_provider", "should_load_provider_and_file_based_skills_together"); + + Path skillsDir = ctx.getWorkDir().resolve("file-skills"); + Files.createDirectories(skillsDir.resolve("file-notes")); + Files.writeString(skillsDir.resolve("file-notes").resolve("SKILL.md"), + "---\nname: file-notes\ndescription: Reports the file notes verification word.\n---\n\nThe file notes verification word is MAPLE_FALCON_27.\n"); + + var provider = new TestSkillProvider(List.of(skill("provider-audit", + "Reports the provider audit verification word.", + "---\nname: provider-audit\nallowed-tools: view\n---\n\nThe provider audit verification word is COBALT_HERON_58.\n"))); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client + .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setSkillDirectories(List.of(skillsDir.toString())).setSkillProvider(provider)) + .get(30, TimeUnit.SECONDS); + + try { + List skills = session.getRpc().skills.list().get(30, TimeUnit.SECONDS).skills(); + Skill fileSkill = findSkill(skills, "file-notes"); + Skill providerSkill = findSkill(skills, "provider-audit"); + assertNotNull(fileSkill, "Expected file-notes to be listed"); + assertNotEquals(SkillSource.SDK, fileSkill.source()); + assertNotNull(fileSkill.path()); + assertNotNull(providerSkill, "Expected provider-audit to be listed"); + assertEquals(SkillSource.SDK, providerSkill.source()); + + AssistantMessageEvent message = session.sendAndWait(new MessageOptions().setPrompt( + "Use the skill tool to load the file-notes skill and the provider-audit skill, then reply with both verification words."), + 60_000).get(90, TimeUnit.SECONDS); + + assertEquals(List.of("provider-audit"), provider.reads()); + assertTrue(assistantText(message).contains("MAPLE_FALCON_27")); + // Validate the final assistant response arrived (guards against truncated + // captures) + assertTrue(assistantText(message).contains("COBALT_HERON_58")); + } finally { + session.close(); + } + } + } + + @Test + void should_rebind_skill_provider_on_resume() throws Exception { + ctx.configureForTest("skill_provider", "should_rebind_skill_provider_on_resume"); + + var original = new TestSkillProvider(List.of(skill("rebind-check", "Reports the rebind verification word.", + "The rebind verification word is AMBER_ALPHA_11.\n"))); + var replacement = new TestSkillProvider(List.of(skill("rebind-check", "Reports the rebind verification word.", + "The rebind verification word is BRONZE_BETA_22.\n"))); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession first = client.createSession(new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(original)) + .get(30, TimeUnit.SECONDS); + String sessionId = first.getSessionId(); + AssistantMessageEvent ready = first + .sendAndWait(new MessageOptions() + .setPrompt("Without using any tools or skills, reply with exactly REBIND_READY."), 60_000) + .get(90, TimeUnit.SECONDS); + assertTrue(assistantText(ready).contains("REBIND_READY")); + first.close(); + assertEquals(List.of(), original.reads()); + int originalCallsBeforeResume = original.callCount(); + + CopilotSession session = client + .resumeSession(sessionId, new ResumeSessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(replacement)) + .get(30, TimeUnit.SECONDS); + + try { + AssistantMessageEvent message = session.sendAndWait(new MessageOptions().setPrompt( + "Use the skill tool to load the rebind-check skill, then reply with its verification word."), + 60_000).get(90, TimeUnit.SECONDS); + + assertEquals(List.of("rebind-check"), replacement.reads()); + assertEquals(originalCallsBeforeResume, original.callCount()); + // Validate the final assistant response arrived (guards against truncated + // captures) + assertTrue(assistantText(message).contains("BRONZE_BETA_22")); + assertFalse(assistantText(message).contains("AMBER_ALPHA_11")); + } finally { + session.close(); + } + } + } + + @Test + void should_report_provider_read_failure_without_leaking_details() throws Exception { + ctx.configureForTest("skill_provider", "should_report_provider_read_failure_without_leaking_details"); + + String secret = "PROVIDER_SECRET_7F3A9C"; + var provider = new TestSkillProvider(List.of(new ProvidedSkill(new SkillProviderDescriptor("broken-lookup", + "Reports the broken lookup verification word.", null, null, null), () -> { + throw new IllegalStateException("database unavailable: " + secret); + }))); + List events = new CopyOnWriteArrayList<>(); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client.createSession(new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(provider)) + .get(30, TimeUnit.SECONDS); + session.on(events::add); + + try { + AssistantMessageEvent message = session.sendAndWait(new MessageOptions().setPrompt( + "Use the skill tool to load the broken-lookup skill. If loading fails, reply with exactly LOAD_FAILED."), + 60_000).get(90, TimeUnit.SECONDS); + + assertTrue(provider.reads().contains("broken-lookup")); + List failures = failedToolExecutions(events); + assertEquals(1, failures.size()); + assertFalse(JsonRpcClient.getObjectMapper().writeValueAsString(events).contains(secret)); + // Validate the final assistant response arrived (guards against truncated + // captures) + assertTrue(assistantText(message).contains("LOAD_FAILED")); + } finally { + session.close(); + } + } + } + + @Test + void should_report_missing_provider_skill_as_not_found() throws Exception { + ctx.configureForTest("skill_provider", "should_report_missing_provider_skill_as_not_found"); + + var provider = new TestSkillProvider(List.of(new ProvidedSkill(new SkillProviderDescriptor("vanished-lookup", + "Reports the vanished lookup verification word.", null, null, null), () -> null))); + List events = new CopyOnWriteArrayList<>(); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client.createSession(new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(provider)) + .get(30, TimeUnit.SECONDS); + session.on(events::add); + + try { + AssistantMessageEvent message = session.sendAndWait(new MessageOptions().setPrompt( + "Use the skill tool to load the vanished-lookup skill. If loading fails, reply with exactly LOAD_FAILED."), + 60_000).get(90, TimeUnit.SECONDS); + + assertTrue(provider.reads().contains("vanished-lookup")); + List failures = failedToolExecutions(events); + assertEquals(1, failures.size()); + String failureJson = JsonRpcClient.getObjectMapper().writeValueAsString(failures.get(0)) + .toLowerCase(Locale.ROOT); + assertTrue(failureJson.contains("not found"), "Expected not found failure, got: " + failureJson); + // Validate the final assistant response arrived (guards against truncated + // captures) + assertTrue(assistantText(message).contains("LOAD_FAILED")); + } finally { + session.close(); + } + } + } + + @Test + void should_keep_provider_dormant_when_skills_disabled() throws Exception { + ctx.initializeProxy(); + + var provider = new TestSkillProvider(List.of(skill("dormant-lookup", "Never listed.", "Never read.\n"))); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client + .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setEnableSkills(false).setSkillProvider(provider)) + .get(30, TimeUnit.SECONDS); + + try { + session.getRpc().skills.ensureLoaded().get(30, TimeUnit.SECONDS); + List skills = session.getRpc().skills.list().get(30, TimeUnit.SECONDS).skills(); + + assertEquals(List.of(), sdkSkills(skills)); + assertEquals(List.of(), provider.calls()); + } finally { + session.close(); + } + } + } + + @Test + void should_unbind_provider_when_resumed_without_one() throws Exception { + ctx.initializeProxy(); + + var provider = new TestSkillProvider( + List.of(skill("unbound-lookup", "Reports the unbound lookup word.", "Unbound.\n"))); + + try (CopilotClient client = ctx.createClient()) { + CopilotSession first = client.createSession(new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(provider)) + .get(30, TimeUnit.SECONDS); + List before = first.getRpc().skills.list().get(30, TimeUnit.SECONDS).skills(); + assertTrue(before.stream().anyMatch(s -> "unbound-lookup".equals(s.name()))); + int callsBeforeResume = provider.callCount(); + + CopilotSession session = client + .resumeSession(first.getSessionId(), + new ResumeSessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)) + .get(30, TimeUnit.SECONDS); + + try { + session.getRpc().skills.reload().get(30, TimeUnit.SECONDS); + List skills = session.getRpc().skills.list().get(30, TimeUnit.SECONDS).skills(); + + assertEquals(List.of(), sdkSkills(skills)); + assertEquals(callsBeforeResume, provider.callCount()); + } finally { + session.close(); + } + } + } + + @Test + void should_cancel_a_blocked_provider_call_when_the_session_disconnects() throws Exception { + ctx.initializeProxy(); + + var entered = new CountDownLatch(1); + var pending = new CompletableFuture>(); + var provider = new SkillProvider() { + @Override + public CompletableFuture> listSkills() { + entered.countDown(); + return pending; + } + + @Override + public CompletableFuture readSkill(String name) { + return CompletableFuture.completedFuture(null); + } + }; + + try (CopilotClient client = ctx.createClient()) { + CopilotSession session = client.createSession(new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setSkillProvider(provider)) + .get(30, TimeUnit.SECONDS); + + // The list RPC fails once the binding is removed; only the provider's + // cancellation matters here. + session.getRpc().skills.list(); + assertTrue(entered.await(30, TimeUnit.SECONDS), "provider listSkills was not called"); + + session.close(); + assertThrows(CancellationException.class, () -> pending.get(10, TimeUnit.SECONDS)); + } + } + + @Test + void should_reject_skill_provider_for_cloud_sessions() throws Exception { + ctx.initializeProxy(); + + var provider = new TestSkillProvider(List.of(skill("cloud-lookup", "Never listed.", "Never read.\n"))); + + try (CopilotClient client = ctx.createClient()) { + var config = new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL) + .setCloud(new CloudSessionOptions()).setSkillProvider(provider); + + ExecutionException error = assertThrows(ExecutionException.class, + () -> client.createSession(config).get(30, TimeUnit.SECONDS)); + assertInstanceOf(IllegalArgumentException.class, error.getCause()); + assertEquals("Skill providers are not supported for cloud sessions.", error.getCause().getMessage()); + assertEquals(List.of(), provider.calls()); + } + } + + private static ProvidedSkill skill(String name, String description, String markdown) { + return new ProvidedSkill(new SkillProviderDescriptor(name, description, null, null, null), () -> markdown); + } + + private static Skill findSkill(List skills, String name) { + return skills.stream().filter(s -> name.equals(s.name())).findFirst().orElse(null); + } + + private static List sdkSkills(List skills) { + return skills.stream().filter(s -> s.source() == SkillSource.SDK).toList(); + } + + private static List failedToolExecutions(List events) { + return events.stream().filter(ToolExecutionCompleteEvent.class::isInstance) + .map(ToolExecutionCompleteEvent.class::cast) + .filter(event -> !Boolean.TRUE.equals(event.getData().success())).toList(); + } + + private static String assistantText(AssistantMessageEvent response) { + assertNotNull(response, "Expected a response from the assistant"); + assertNotNull(response.getData(), "Expected assistant response data"); + assertNotNull(response.getData().content(), "Expected assistant response content"); + return response.getData().content(); + } + + private record ProvidedSkill(SkillProviderDescriptor descriptor, Supplier reader) { + String read() { + return reader.get(); + } + } + + private static final class TestSkillProvider implements SkillProvider { + private final List calls = new CopyOnWriteArrayList<>(); + private final List skills; + + private TestSkillProvider(List skills) { + this.skills = skills; + } + + List calls() { + return List.copyOf(calls); + } + + int callCount() { + return calls.size(); + } + + List reads() { + return calls.stream().filter(call -> call.startsWith("read:")).map(call -> call.substring("read:".length())) + .toList(); + } + + @Override + public CompletableFuture> listSkills() { + calls.add("list"); + return CompletableFuture.completedFuture(skills.stream().map(ProvidedSkill::descriptor).toList()); + } + + @Override + public CompletableFuture readSkill(String name) { + calls.add("read:" + name); + return CompletableFuture + .completedFuture(skills.stream().filter(skill -> skill.descriptor().name().equals(name)).findFirst() + .map(ProvidedSkill::read).orElse(null)); + } + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/StdioShutdownIT.java b/java/sdk/src/test/java/com/github/copilot/StdioShutdownIT.java index ed3200e671..9bfb98c277 100644 --- a/java/sdk/src/test/java/com/github/copilot/StdioShutdownIT.java +++ b/java/sdk/src/test/java/com/github/copilot/StdioShutdownIT.java @@ -7,8 +7,15 @@ import com.github.copilot.rpc.CopilotClientOptions; import java.nio.file.Files; +import java.nio.file.FileSystems; import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Comparator; +import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -42,6 +49,72 @@ void closeWaitsForCleanupAfterStdinEof() throws Exception { } } + @Test + void failedShutdownStillWaitsForCleanupAfterStdinEof() throws Exception { + try (var fixture = new Fixture("shutdown-error"); var watcher = FileSystems.getDefault().newWatchService()) { + fixture.client.start().get(30, TimeUnit.SECONDS); + fixture.directory.register(watcher, StandardWatchEventKinds.ENTRY_CREATE); + Path eof = fixture.marker.resolveSibling(fixture.marker.getFileName() + ".eof"); + Path release = fixture.marker.resolveSibling(fixture.marker.getFileName() + ".release"); + var stopping = fixture.client.stop(); + long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5); + while (!Files.isRegularFile(eof)) { + var key = watcher.poll(Math.max(0, deadline - System.nanoTime()), TimeUnit.NANOSECONDS); + assertNotNull(key, "Failed shutdown must still deliver stdin EOF before terminating the runtime"); + key.pollEvents(); + assertTrue(key.reset(), "The owned cleanup directory must remain available"); + } + assertFalse(stopping.isDone(), "Shutdown must not return before EOF-driven cleanup completes"); + Files.writeString(release, ""); + stopping.get(60, TimeUnit.SECONDS); + fixture.assertCleanExit(); + } + } + + @Test + void stopPreservesDetachedServiceAfterLauncherAndRuntimeExit() throws Exception { + try (var fixture = new Fixture("stop", true, true)) { + fixture.client.start().get(30, TimeUnit.SECONDS); + fixture.assertServiceResponsive(); + fixture.client.stop().get(60, TimeUnit.SECONDS); + fixture.assertCleanExit(); + fixture.assertServiceResponsive(); + } + } + + @Test + void closePreservesDetachedServiceAfterLauncherAndRuntimeExit() throws Exception { + try (var fixture = new Fixture("dispose", true, true)) { + fixture.client.start().get(30, TimeUnit.SECONDS); + fixture.assertServiceResponsive(); + fixture.client.close(); + fixture.assertCleanExit(); + fixture.assertServiceResponsive(); + } + } + + @Test + void stopPreservesDetachedTcpServiceWithoutPeerEof() throws Exception { + try (var fixture = new Fixture("stop", true, true, false)) { + fixture.client.start().get(30, TimeUnit.SECONDS); + fixture.assertServiceResponsive(); + fixture.client.stop().get(60, TimeUnit.SECONDS); + fixture.assertCleanExit(); + fixture.assertServiceResponsive(); + } + } + + @Test + void closePreservesDetachedTcpServiceWithoutPeerEof() throws Exception { + try (var fixture = new Fixture("dispose", true, true, false)) { + fixture.client.start().get(30, TimeUnit.SECONDS); + fixture.assertServiceResponsive(); + fixture.client.close(); + fixture.assertCleanExit(); + fixture.assertServiceResponsive(); + } + } + @Test void forceStopDoesNotWaitForGracefulCleanup() throws Exception { try (var fixture = new Fixture("force")) { @@ -76,6 +149,15 @@ void closeTerminatesChildThatDoesNotExitAfterEof() throws Exception { } } + @Test + void stopTerminatesLauncherAndItsChildAfterEof() throws Exception { + try (var fixture = new Fixture("fallback", true)) { + fixture.client.start().get(30, TimeUnit.SECONDS); + fixture.client.stop().get(60, TimeUnit.SECONDS); + fixture.assertCleanExit(); + } + } + @Test void failedStartupTerminatesChild() throws Exception { try (var fixture = new Fixture("start-failure")) { @@ -89,16 +171,96 @@ private static final class Fixture implements AutoCloseable { private final Path directory; private final Path marker; private final Path pid; + private final Path launcherPid; + private final Path servicePid; + private final Path servicePort; private final CopilotClient client; private Fixture(String mode) throws Exception { + this(mode, false); + } + + private Fixture(String mode, boolean useLauncher) throws Exception { + this(mode, useLauncher, false); + } + + private Fixture(String mode, boolean useLauncher, boolean detachedService) throws Exception { + this(mode, useLauncher, detachedService, true); + } + + private Fixture(String mode, boolean useLauncher, boolean detachedService, boolean useStdio) throws Exception { directory = Files.createDirectories(Path.of("target", "shutdown-" + UUID.randomUUID()).toAbsolutePath()); marker = directory.resolve("cleanup.jsonl"); pid = directory.resolve("pid"); + launcherPid = useLauncher ? directory.resolve("launcher-pid") : null; + servicePid = detachedService ? directory.resolve("service-pid") : null; + servicePort = detachedService ? directory.resolve("service-port") : null; Path script = Path.of("..", "..", "test", "harness", "stdio-shutdown-runtime.cjs").toAbsolutePath(); assertTrue(Files.isRegularFile(script), "Shared shutdown fixture must exist"); - client = new CopilotClient(new CopilotClientOptions().setAutoStart(false).setCliPath("node") - .setCliArgs(new String[]{script.toString(), marker.toString(), mode, pid.toString()})); + String nodePath = TestUtil.findExecutableInPath("node"); + assertNotNull(nodePath, "Node.js was not found in PATH"); + var args = new ArrayList(); + if (useLauncher) { + Path launcher = directory.resolve("launcher.cjs"); + Files.writeString(launcher, """ + const { spawn } = require("node:child_process"); + require("node:fs").writeFileSync(process.argv[2], String(process.pid)); + const args = process.argv.slice(3); + const fail = error => { console.error(error); process.exit(1); }; + const launch = () => { + if (args[0] === "--in-process-runtime") { + args.shift(); + const script = args.shift(); + process.argv = [process.execPath, script, ...args]; + require(script); + return; + } + const child = spawn(process.execPath, args, { stdio: "inherit" }); + child.on("error", fail); + child.on("exit", code => process.exit(code ?? 1)); + }; + if (args[0] === "--detached-service") { + args.shift(); + const service = spawn(process.execPath, ["-e", ` + const fs = require("node:fs"); + const server = require("node:net").createServer(socket => { + socket.write(process.argv[3] + ":"); + socket.pipe(socket); + }); + server.listen(0, "127.0.0.1", () => { + fs.writeFileSync(process.argv[1], String(process.pid)); + fs.writeFileSync(process.argv[2], String(server.address().port)); + process.send("ready"); + process.disconnect(); + }); + `, args.shift(), args.shift(), args.shift()], + { detached: true, stdio: ["ignore", "ignore", "ignore", "ipc"] }); + service.on("error", fail); + service.once("message", message => { + if (message !== "ready") return fail(new Error("Unexpected service handshake")); + service.unref(); + launch(); + }); + } else { + launch(); + } + """); + args.add(launcher.toString()); + args.add(launcherPid.toString()); + if (detachedService) { + args.addAll(List.of("--detached-service", servicePid.toString(), servicePort.toString(), + directory.getFileName().toString())); + } + } + if (useLauncher && !useStdio) { + args.add("--in-process-runtime"); + } + args.addAll(List.of(script.toString(), marker.toString(), mode, pid.toString())); + if (!useStdio) { + args.add("--fixture-tcp"); + } + client = new CopilotClient(new CopilotClientOptions().setAutoStart(false).setCliPath(nodePath) + .setCliArgs(args.toArray(String[]::new)).setUseStdio(useStdio)); } private void assertCleanExit() throws Exception { @@ -106,7 +268,29 @@ private void assertCleanExit() throws Exception { assertExited(); } + private void assertServiceResponsive() throws Exception { + assertTrue(Files.isRegularFile(servicePort), "The detached service must acknowledge readiness"); + try (var socket = new Socket()) { + socket.connect(new InetSocketAddress("127.0.0.1", Integer.parseInt(Files.readString(servicePort))), + 5000); + socket.setSoTimeout(5000); + String nonce = UUID.randomUUID().toString(); + byte[] expected = (directory.getFileName() + ":" + nonce).getBytes(StandardCharsets.UTF_8); + socket.getOutputStream().write(nonce.getBytes(StandardCharsets.UTF_8)); + socket.getOutputStream().flush(); + assertArrayEquals(expected, socket.getInputStream().readNBytes(expected.length), + "The detached service must still serve requests, not merely retain a PID"); + } + } + private void assertExited() throws Exception { + assertExited(pid); + if (launcherPid != null) { + assertExited(launcherPid); + } + } + + private static void assertExited(Path pid) throws Exception { assertTrue(Files.isRegularFile(pid), "Child must have started"); assertFalse( ProcessHandle.of(Long.parseLong(Files.readString(pid))).map(ProcessHandle::isAlive).orElse(false), @@ -115,11 +299,21 @@ private void assertExited() throws Exception { @Override public void close() throws Exception { - if (Files.isRegularFile(pid)) { - var process = ProcessHandle.of(Long.parseLong(Files.readString(pid))); - if (process.isPresent() && process.get().isAlive()) { - process.get().destroyForcibly(); - process.get().onExit().get(10, TimeUnit.SECONDS); + var ownedPids = new ArrayList(); + if (servicePid != null) { + ownedPids.add(servicePid); + } + ownedPids.add(pid); + if (launcherPid != null) { + ownedPids.add(launcherPid); + } + for (var ownedPid : ownedPids) { + if (Files.isRegularFile(ownedPid)) { + var process = ProcessHandle.of(Long.parseLong(Files.readString(ownedPid))); + if (process.isPresent() && process.get().isAlive()) { + process.get().destroyForcibly(); + process.get().onExit().get(10, TimeUnit.SECONDS); + } } } client.close(); diff --git a/java/sdk/src/test/java/com/github/copilot/StreamingFidelityTest.java b/java/sdk/src/test/java/com/github/copilot/StreamingFidelityTest.java index c506d23522..903d18ae3d 100644 --- a/java/sdk/src/test/java/com/github/copilot/StreamingFidelityTest.java +++ b/java/sdk/src/test/java/com/github/copilot/StreamingFidelityTest.java @@ -8,7 +8,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; @@ -65,7 +64,7 @@ void testShouldProduceDeltaEventsWhenStreamingIsEnabled() throws Exception { CopilotSession session = client.createSession( new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setStreaming(true)).get(); - List events = new ArrayList<>(); + List events = new CopyOnWriteArrayList<>(); session.on(events::add); session.sendAndWait(new MessageOptions().setPrompt("Count from 1 to 5, separated by commas.")).get(60, @@ -112,7 +111,7 @@ void testShouldNotProduceDeltasWhenStreamingIsDisabled() throws Exception { new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setStreaming(false)) .get(); - List events = new ArrayList<>(); + List events = new CopyOnWriteArrayList<>(); session.on(events::add); session.sendAndWait(new MessageOptions().setPrompt("Say 'hello world'.")).get(60, TimeUnit.SECONDS); @@ -213,7 +212,7 @@ void testShouldNotProduceDeltasAfterSessionResumeWithStreamingDisabled() throws CopilotSession session2 = newClient.resumeSession(sessionId, new ResumeSessionConfig() .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setStreaming(false)).get(); - List events = new ArrayList<>(); + List events = new CopyOnWriteArrayList<>(); session2.on(events::add); AssistantMessageEvent answer = session2 @@ -260,7 +259,7 @@ void testShouldEmitStreamingDeltasWithReasoningEffortConfigured() throws Excepti .setModel("gpt-5.4").setStreaming(true).setReasoningEffort("high")) .get(); - List events = new ArrayList<>(); + List events = new CopyOnWriteArrayList<>(); session.on(events::add); session.sendAndWait(new MessageOptions().setPrompt("What is 15 * 17?")).get(60, TimeUnit.SECONDS); diff --git a/java/sdk/src/test/java/com/github/copilot/StringSchemaToolsIT.java b/java/sdk/src/test/java/com/github/copilot/StringSchemaToolsIT.java new file mode 100644 index 0000000000..3549b6b3d6 --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/StringSchemaToolsIT.java @@ -0,0 +1,49 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.*; + +import java.nio.file.Files; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; + +import com.github.copilot.rpc.MessageOptions; +import com.github.copilot.rpc.PermissionHandler; +import com.github.copilot.rpc.SessionConfig; +import com.github.copilot.rpc.ToolDefinition; + +class StringSchemaToolsIT { + @Test + void stringSchemaApplyPatchOverrideBindsPatchInput() throws Exception { + try (E2ETestContext ctx = E2ETestContext.create()) { + ctx.configureForTest("tools", "string_schema_apply_patch_override_binds_patch_input"); + var receivedPatch = new CompletableFuture(); + var tool = ToolDefinition.create("apply_patch", "Apply a patch", Map.of("type", "string"), invocation -> { + receivedPatch.complete(invocation.getArgumentsAs(String.class)); + return CompletableFuture.completedFuture("HOST_PATCH_HANDLED"); + }).overridesBuiltInTool(true); + try (CopilotClient client = ctx.createClient(); + CopilotSession session = client.createSession(new SessionConfig() + .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setTools(List.of(tool))) + .get(30, TimeUnit.SECONDS)) { + var response = session + .sendAndWait(new MessageOptions().setPrompt("Use apply_patch to apply the supplied patch.")) + .get(60, TimeUnit.SECONDS); + + assertTrue(receivedPatch.isDone(), "Typed string handler was not invoked"); + assertEquals("*** Begin Patch\n*** Add File: override-marker.txt\n+from-native\n*** End Patch", + receivedPatch.get(10, TimeUnit.SECONDS)); + assertNotNull(response); + assertEquals("Host override completed.", response.getData().content()); + assertFalse(Files.exists(ctx.getWorkDir().resolve("override-marker.txt"))); + } + } + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/SubagentHooksE2ETest.java b/java/sdk/src/test/java/com/github/copilot/SubagentHooksE2ETest.java index 009773bb54..bc83502f87 100644 --- a/java/sdk/src/test/java/com/github/copilot/SubagentHooksE2ETest.java +++ b/java/sdk/src/test/java/com/github/copilot/SubagentHooksE2ETest.java @@ -4,16 +4,18 @@ package com.github.copilot; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.InputStream; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashMap; import java.util.List; import java.util.UUID; @@ -23,6 +25,8 @@ import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.github.copilot.generated.AssistantMessageEvent; import com.github.copilot.rpc.CopilotClientOptions; import com.github.copilot.rpc.MessageOptions; @@ -31,20 +35,33 @@ import com.github.copilot.rpc.PreToolUseHookOutput; import com.github.copilot.rpc.SessionConfig; import com.github.copilot.rpc.SessionHooks; +import com.github.copilot.rpc.SubagentStartHookInput; +import com.github.copilot.rpc.SubagentStartHookOutput; +import com.github.copilot.rpc.SubagentStopHookInput; +import com.github.copilot.rpc.SubagentStopHookOutput; public class SubagentHooksE2ETest { - private static final String SNAPSHOT_NAME = "should_invoke_pretooluse_and_posttooluse_hooks_for_sub_agent_tool_calls"; + private static final String SNAPSHOT_NAME = "should_apply_subagent_lifecycle_hook_outputs"; + private static final String CHILD_CONTEXT = "Subagent start hook verified: read the requested file."; + private static final String STOP_RESPONSE_PREFIX = "Subagent stop hook verified: "; @Test - void shouldInvokePreToolUseAndPostToolUseHooksForSubAgentToolCalls() throws Exception { + void shouldApplySubagentLifecycleHookOutputs() throws Exception { try (E2ETestContext ctx = E2ETestContext.create()) { ctx.configureForTest("subagent_hooks", SNAPSHOT_NAME); ConcurrentLinkedQueue hookLog = new ConcurrentLinkedQueue<>(); + ConcurrentLinkedQueue subagentStarts = new ConcurrentLinkedQueue<>(); + ConcurrentLinkedQueue subagentStops = new ConcurrentLinkedQueue<>(); + ConcurrentLinkedQueue startInvocationIds = new ConcurrentLinkedQueue<>(); + ConcurrentLinkedQueue stopInvocationIds = new ConcurrentLinkedQueue<>(); + CompletableFuture subagentStopped = new CompletableFuture<>(); RecordingForwardingRequestHandler requestHandler = new RecordingForwardingRequestHandler(); - String waitingText = "I've launched an explore agent to read subagent-test.txt. Waiting for it to complete..."; - String finalText = "The explore agent successfully read the file. The contents of **subagent-test.txt** are:\n\n```\nHello from subagent test!\n```"; + String waitingText = "I've launched an explore agent to read subagent-test.txt. " + + "Waiting for it to complete..."; + String finalText = "The explore agent successfully read the file. " + + "The contents of **subagent-test.txt** are:\n\n```\nHello from subagent test!\n```"; String parentSessionId = UUID.randomUUID().toString(); CompletableFuture parentWaiting = new CompletableFuture<>(); HashMap env = new HashMap<>(ctx.getEnvironment()); @@ -65,6 +82,16 @@ void shouldInvokePreToolUseAndPostToolUseHooksForSubAgentToolCalls() throws Exce return parentWaiting.thenApply(ignored -> null); } return CompletableFuture.completedFuture((PostToolUseHookOutput) null); + }).setOnSubagentStart((input, invocation) -> { + subagentStarts.add(input); + startInvocationIds.add(invocation.getSessionId()); + return CompletableFuture.completedFuture(new SubagentStartHookOutput(CHILD_CONTEXT)); + }).setOnSubagentStop((input, invocation) -> { + subagentStops.add(input); + stopInvocationIds.add(invocation.getSessionId()); + subagentStopped.complete(input); + return CompletableFuture.completedFuture( + new SubagentStopHookOutput(null, null, STOP_RESPONSE_PREFIX + input.response())); }))).get(); try (var subscription = session.on(AssistantMessageEvent.class, message -> { if ((message.getAgentId() == null || message.getAgentId().isEmpty()) @@ -72,43 +99,111 @@ void shouldInvokePreToolUseAndPostToolUseHooksForSubAgentToolCalls() throws Exce parentWaiting.complete(null); } })) { - Files.writeString(ctx.getWorkDir().resolve("subagent-test.txt"), "Hello from subagent test!"); - var response = session.sendAndWait(new MessageOptions() - .setPrompt("Use the task tool to spawn an explore agent that reads the file " - + "subagent-test.txt in the current directory and reports its contents. " - + "You must use the task tool.")) - .get(120, TimeUnit.SECONDS); - assertNotNull(response); - assertTrue(response.getAgentId() == null || response.getAgentId().isEmpty()); - assertEquals(finalText, response.getData().content()); - var replies = session.getMessages().get().stream() - .filter(event -> event instanceof AssistantMessageEvent - && (event.getAgentId() == null || event.getAgentId().isEmpty())) - .map(event -> ((AssistantMessageEvent) event).getData().content()) - .filter(content -> waitingText.equals(content) || finalText.equals(content)).toList(); - assertEquals(List.of(waitingText, finalText), replies, - "Durable history must contain the waiting reply before the final reply"); - - HookEntry taskPre = hookLog.stream() - .filter(h -> h.kind().equals("pre") && h.toolName().equals("task")).findFirst() - .orElse(null); - assertNotNull(taskPre, "preToolUse should fire for the parent's 'task' tool call"); - - List viewPre = hookLog.stream() - .filter(h -> h.kind().equals("pre") && h.toolName().equals("view")).toList(); - List viewPost = hookLog.stream() - .filter(h -> h.kind().equals("post") && h.toolName().equals("view")).toList(); - assertFalse(viewPre.isEmpty(), "preToolUse should fire for the sub-agent's 'view' tool call"); - assertFalse(viewPost.isEmpty(), "postToolUse should fire for the sub-agent's 'view' tool call"); - assertNotEquals(taskPre.sessionId(), viewPre.get(0).sessionId(), - "Sub-agent tool hooks should have a different sessionId than parent tool hooks"); - assertSubagentRequestMetadata(requestHandler.inferenceRequests()); - } finally { - parentWaiting.complete(null); - session.close(); + try { + Files.writeString(ctx.getWorkDir().resolve("subagent-test.txt"), "Hello from subagent test!"); + var response = session.sendAndWait(new MessageOptions() + .setPrompt("Use the task tool to spawn an explore agent that reads the file " + + "subagent-test.txt in the current directory and reports its contents. " + + "You must use the task tool.")) + .get(120, TimeUnit.SECONDS); + assertNotNull(response); + assertTrue(response.getAgentId() == null || response.getAgentId().isEmpty()); + assertEquals(finalText, response.getData().content()); + var replies = session.getMessages().get().stream() + .filter(event -> event instanceof AssistantMessageEvent + && (event.getAgentId() == null || event.getAgentId().isEmpty())) + .map(event -> ((AssistantMessageEvent) event).getData().content()) + .filter(content -> waitingText.equals(content) || finalText.equals(content)).toList(); + assertEquals(List.of(waitingText, finalText), replies, + "Durable history must contain the waiting reply before the final reply"); + subagentStopped.get(120, TimeUnit.SECONDS); + + HookEntry taskPre = hookLog.stream() + .filter(h -> h.kind().equals("pre") && h.toolName().equals("task")).findFirst() + .orElse(null); + assertNotNull(taskPre, "preToolUse should fire for the parent's 'task' tool call"); + + List viewPre = hookLog.stream() + .filter(h -> h.kind().equals("pre") && h.toolName().equals("view")).toList(); + List viewPost = hookLog.stream() + .filter(h -> h.kind().equals("post") && h.toolName().equals("view")).toList(); + assertFalse(viewPre.isEmpty(), "preToolUse should fire for the sub-agent's 'view' tool call"); + assertFalse(viewPost.isEmpty(), "postToolUse should fire for the sub-agent's 'view' tool call"); + assertNotEquals(taskPre.sessionId(), viewPre.get(0).sessionId(), + "Sub-agent tool hooks should have a different sessionId than parent tool hooks"); + assertSubagentRequestMetadata(requestHandler.inferenceRequests()); + ObjectMapper mapper = new ObjectMapper(); + boolean childContextObserved = false; + for (RequestRecord request : requestHandler.inferenceRequests()) { + if (request.parentAgentId() == null || request.parentAgentId().isEmpty()) { + continue; + } + for (JsonNode message : mapper.readTree(request.body()).path("messages")) { + if (hasChildContext(message)) { + childContextObserved = true; + } + } + } + assertTrue(childContextObserved, + "start hook context should be prepended to the child inference prompt"); + assertTrue( + requestHandler.inferenceRequests().stream() + .anyMatch(r -> (r.parentAgentId() == null || r.parentAgentId().isEmpty()) + && r.body().contains(STOP_RESPONSE_PREFIX)), + "rewritten stop response should reach a parent inference request"); + + assertEquals(1, subagentStarts.size(), "one start hook per launched subagent"); + assertEquals(List.of(session.getSessionId()), List.copyOf(startInvocationIds)); + SubagentStartHookInput start = subagentStarts.element(); + assertEquals(session.getSessionId(), start.sessionId()); + assertTrue(start.timestamp() > 0); + assertEquals(ctx.getWorkDir().toRealPath(), Path.of(start.cwd()).toRealPath()); + assertNotNull(start.transcriptPath()); + assertEquals("explore", start.agentName()); + assertNull(start.agentDisplayName()); + assertNull(start.agentDescription()); + + assertEquals(1, subagentStops.size(), "one stop hook per completed subagent"); + assertEquals(List.of(session.getSessionId()), List.copyOf(stopInvocationIds)); + SubagentStopHookInput stop = subagentStops.element(); + assertEquals(start.sessionId(), stop.sessionId()); + assertTrue(stop.timestamp() >= start.timestamp()); + assertEquals(start.cwd(), stop.cwd()); + assertEquals(start.transcriptPath(), stop.transcriptPath()); + assertEquals(start.agentName(), stop.agentName()); + assertEquals(start.agentDisplayName(), stop.agentDisplayName()); + assertEquals(start.agentDescription(), stop.agentDescription()); + assertEquals("explore", stop.agentType()); + assertNotNull(stop.agentId()); + assertFalse(stop.agentId().isBlank()); + assertEquals("end_turn", stop.stopReason()); + assertTrue(stop.response().contains("Hello from subagent test!")); + } finally { + parentWaiting.complete(null); + session.close(); + } + } + } + } + } + + private static boolean hasChildContext(JsonNode message) { + if (!"user".equals(message.path("role").asText())) { + return false; + } + String expected = CHILD_CONTEXT + "\n\nRead the file \"subagent-test.txt\""; + JsonNode content = message.path("content"); + if (content.isTextual()) { + return content.asText().contains(expected); + } + if (content.isArray()) { + for (JsonNode part : content) { + if ("text".equals(part.path("type").asText()) && part.path("text").asText().contains(expected)) { + return true; } } } + return false; } private static void assertSubagentRequestMetadata(List records) { @@ -132,7 +227,8 @@ private static boolean isInferenceUrl(String url) { private record HookEntry(String kind, String toolName, String sessionId) { } - private record RequestRecord(String url, String agentId, String parentAgentId, String interactionType) { + private record RequestRecord(String url, String agentId, String parentAgentId, String interactionType, + String body) { } private static final class RecordingForwardingRequestHandler extends CopilotRequestHandler { @@ -146,7 +242,10 @@ List inferenceRequests() { protected HttpResponse sendRequest(HttpRequest request, CopilotRequestContext ctx) throws Exception { records.add(new RequestRecord(request.uri().toString(), ctx.agentId(), ctx.parentAgentId(), - ctx.interactionType())); + ctx.interactionType(), + isInferenceUrl(request.uri().toString()) + ? CopilotRequestTestSupport.requestBodyText(request) + : "")); return super.sendRequest(request, ctx); } } diff --git a/java/sdk/src/test/java/com/github/copilot/TestUtil.java b/java/sdk/src/test/java/com/github/copilot/TestUtil.java index 8e90c2f48f..b8ac48748e 100644 --- a/java/sdk/src/test/java/com/github/copilot/TestUtil.java +++ b/java/sdk/src/test/java/com/github/copilot/TestUtil.java @@ -95,7 +95,7 @@ static String preparePinnedCli(Path repoRoot) throws Exception { return output; } - private static String findExecutableInPath(String name) { + static String findExecutableInPath(String name) { var pathValue = System.getenv("PATH"); if (pathValue == null || pathValue.isEmpty()) { return null; diff --git a/java/sdk/src/test/java/com/github/copilot/TimeoutEdgeCaseTest.java b/java/sdk/src/test/java/com/github/copilot/TimeoutEdgeCaseTest.java index 01530bc25f..4eee8f5f97 100644 --- a/java/sdk/src/test/java/com/github/copilot/TimeoutEdgeCaseTest.java +++ b/java/sdk/src/test/java/com/github/copilot/TimeoutEdgeCaseTest.java @@ -4,7 +4,10 @@ package com.github.copilot; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.ByteArrayOutputStream; @@ -16,6 +19,8 @@ import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; @@ -84,10 +89,7 @@ public synchronized void flush() throws IOException { * After {@code close()}, the future returned by {@code sendAndWait} must NOT be * completed by a stale timeout. *

    - * Contract: {@code close()} shuts down the timeout scheduler before the - * blocking {@code session.detach} RPC call, so any pending timeout task is - * cancelled and the future remains incomplete (not exceptionally completed with - * {@code TimeoutException}). + * Closing fails the pending response with {@code IllegalStateException}. */ @Test void testTimeoutDoesNotFireAfterSessionClose() throws Exception { @@ -100,12 +102,12 @@ void testTimeoutDoesNotFireAfterSessionClose() throws Exception { assertFalse(result.isDone(), "Future should be pending before timeout fires"); - // close() blocks up to 5s on session.detach RPC. The 2s timeout - // fires during that window with the current per-call scheduler. session.close(); - assertFalse(result.isDone(), "Future should not be completed by a timeout after session is closed. " - + "The per-call ScheduledExecutorService leaked a TimeoutException."); + ExecutionException error = assertThrows(ExecutionException.class, + () -> result.get(5, TimeUnit.SECONDS)); + IllegalStateException failure = assertInstanceOf(IllegalStateException.class, error.getCause()); + assertEquals("Session closed before response completed", failure.getMessage()); } } finally { rpc.close(); diff --git a/java/sdk/src/test/java/com/github/copilot/ToolInvocationIdentityTest.java b/java/sdk/src/test/java/com/github/copilot/ToolInvocationIdentityTest.java new file mode 100644 index 0000000000..116aeeb2cd --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/ToolInvocationIdentityTest.java @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; + +import com.github.copilot.generated.ExternalToolRequestedEvent; +import com.github.copilot.rpc.ToolDefinition; +import com.github.copilot.rpc.ToolInvocation; + +/** + * Verifies that a tool handler learns which sub-agent and runtime request are + * behind an external tool call. + */ +public class ToolInvocationIdentityTest { + + @Test + void namesTheSubAgentAndRequestBehindTheCall() throws Exception { + var rpc = mock(JsonRpcClient.class); + when(rpc.invoke(eq("session.detach"), any(), eq(CopilotSession.SessionDetachResponse.class))) + .thenReturn(CompletableFuture.completedFuture(new CopilotSession.SessionDetachResponse(true, null))); + var session = new CopilotSession("session-1", rpc); + var invocations = new LinkedBlockingQueue(); + session.registerTools(List.of(ToolDefinition.create("calc", "Calculates", Map.of(), invocation -> { + invocations.add(invocation); + return new CompletableFuture(); + }))); + + try { + var subAgentCall = externalToolRequested("req-sub-agent", "tc-sub-agent"); + subAgentCall.setAgentId("task-agent-1"); + session.dispatchEvent(subAgentCall); + session.dispatchEvent(externalToolRequested("req-root", "tc-root")); + var blankAgentCall = externalToolRequested("req-blank-agent", "tc-blank-agent"); + blankAgentCall.setAgentId(""); + session.dispatchEvent(blankAgentCall); + + var byToolCallId = new HashMap(); + for (int i = 0; i < 3; i++) { + var invocation = invocations.poll(1, TimeUnit.SECONDS); + assertNotNull(invocation, "tool handler did not start"); + byToolCallId.put(invocation.getToolCallId(), invocation); + } + + var subAgent = byToolCallId.get("tc-sub-agent"); + assertEquals("task-agent-1", subAgent.getAgentId()); + assertEquals("req-sub-agent", subAgent.getRequestId()); + var root = byToolCallId.get("tc-root"); + assertNull(root.getAgentId()); + assertEquals("req-root", root.getRequestId()); + var blankAgent = byToolCallId.get("tc-blank-agent"); + assertNull(blankAgent.getAgentId()); + assertEquals("req-blank-agent", blankAgent.getRequestId()); + } finally { + session.close(); + } + } + + private static ExternalToolRequestedEvent externalToolRequested(String requestId, String toolCallId) { + var event = new ExternalToolRequestedEvent(); + event.setData(new ExternalToolRequestedEvent.ExternalToolRequestedEventData(requestId, "session-1", toolCallId, + "calc", null, Map.of(), null, null, null)); + return event; + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/ToolResultsTest.java b/java/sdk/src/test/java/com/github/copilot/ToolResultsTest.java index 8278fdf28f..cf3db2541f 100644 --- a/java/sdk/src/test/java/com/github/copilot/ToolResultsTest.java +++ b/java/sdk/src/test/java/com/github/copilot/ToolResultsTest.java @@ -6,10 +6,10 @@ import static org.junit.jupiter.api.Assertions.*; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterAll; @@ -75,7 +75,7 @@ void testShouldHandleToolResultWithRejectedResultType() throws Exception { CopilotSession session = client.createSession(new SessionConfig().setTools(List.of(deployTool)) .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); - List events = new ArrayList<>(); + List events = new CopyOnWriteArrayList<>(); session.on(events::add); session.sendAndWait(new MessageOptions().setPrompt( @@ -123,7 +123,7 @@ void testShouldHandleToolResultWithDeniedResultType() throws Exception { CopilotSession session = client.createSession(new SessionConfig().setTools(List.of(accessTool)) .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); - List events = new ArrayList<>(); + List events = new CopyOnWriteArrayList<>(); session.on(events::add); session.sendAndWait(new MessageOptions().setPrompt( diff --git a/java/sdk/src/test/java/com/github/copilot/generated/GeneratedTypesJacksonRoundTripTest.java b/java/sdk/src/test/java/com/github/copilot/generated/GeneratedTypesJacksonRoundTripTest.java index 1b9b99bb57..f1d40ba04d 100644 --- a/java/sdk/src/test/java/com/github/copilot/generated/GeneratedTypesJacksonRoundTripTest.java +++ b/java/sdk/src/test/java/com/github/copilot/generated/GeneratedTypesJacksonRoundTripTest.java @@ -16,6 +16,7 @@ import java.util.List; import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -53,6 +54,21 @@ private static ObjectMapper createMapper() { return mapper; } + @Test + void futureFileEditKindDoesNotRejectToolCompletion() throws IOException { + var event = MAPPER.readValue( + "{\"type\":\"tool.execution_complete\",\"data\":{\"toolCallId\":\"call-1\",\"success\":true," + + "\"fileEdits\":[{\"path\":\"/future.txt\",\"kind\":\"append\"}]}}", + ToolExecutionCompleteEvent.class); + + assertEquals("call-1", event.getData().toolCallId()); + assertEquals("/future.txt", event.getData().fileEdits().get(0).path()); + assertEquals("append", event.getData().fileEdits().get(0).kind().getValue()); + assertEquals("append", MAPPER.readTree(MAPPER.writeValueAsString(event)).path("data").path("fileEdits").get(0) + .path("kind").asText()); + assertEquals(ToolExecutionCompleteFileEditKind.CREATE, ToolExecutionCompleteFileEditKind.fromValue("create")); + } + @TestFactory Collection roundTripAllGeneratedRecords() { List tests = new ArrayList<>(); diff --git a/java/sdk/src/test/java/com/github/copilot/generated/rpc/CustomizationReloadOutcomeTest.java b/java/sdk/src/test/java/com/github/copilot/generated/rpc/CustomizationReloadOutcomeTest.java new file mode 100644 index 0000000000..e8c583024c --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/generated/rpc/CustomizationReloadOutcomeTest.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.generated.rpc; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +class CustomizationReloadOutcomeTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + @Test + void futureReloadValuesRoundTripWithoutChangingKnownValues() throws Exception { + var wire = MAPPER.readTree(""" + {"status":"newStatus","subsystem":"newSubsystem","detail":"new component"} + """); + var outcome = MAPPER.treeToValue(wire, CustomizationReloadOutcome.class); + + assertEquals("newStatus", outcome.status().getValue()); + assertEquals("newSubsystem", outcome.subsystem().getValue()); + assertEquals("new component", outcome.detail()); + assertEquals(wire, MAPPER.valueToTree(outcome)); + assertEquals(CustomizationReloadStatus.RELOADED, CustomizationReloadStatus.fromValue("reloaded")); + assertEquals(CustomizationReloadSubsystem.SKILLS, CustomizationReloadSubsystem.fromValue("skills")); + assertEquals("\"reloaded\"", MAPPER.writeValueAsString(CustomizationReloadStatus.RELOADED)); + assertEquals("\"skills\"", MAPPER.writeValueAsString(CustomizationReloadSubsystem.SKILLS)); + } +} diff --git a/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java b/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java index c738d6673d..a7ede09723 100644 --- a/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java +++ b/java/sdk/src/test/java/com/github/copilot/generated/rpc/GeneratedRpcApiCoverageTest.java @@ -63,164 +63,184 @@ void newRpcEndpoints_dispatch_expected_method_params_and_result_type(String expe } static Stream newRpcDispatches() { - return Stream.of( - dispatch("catalog.select", CatalogSelectionResult.class, """ - {"contract":{"protocolVersion":3,"requiredCapabilities":["catalog-selection"]}, - "sessionId":"catalog-session","selectionRef":"selection-1","outcome":"selected"} - """, - (server, session) -> server.catalog.select( - new CatalogSelectParams(new CatalogClientContract(3L, List.of("catalog-selection")), - "catalog-session", "selection-1", CatalogSelectionDecision.SELECTED))), - dispatch("session.managedSettings.get", SessionManagedSettingsGetResult.class, """ + return Stream + .of(dispatch("session.mcp.list", SessionMcpListResult.class, """ {"sessionId":"bound-session"} - """, (server, session) -> session.managedSettings.get()), - dispatch("session.instructions.reload", Void.class, """ - {"sessionId":"bound-session"} - """, (server, session) -> session.instructions.reload()), - dispatch("session.customizations.reload", SessionCustomizationsReloadResult.class, """ - {"sessionId":"bound-session"} - """, (server, session) -> session.customizations.reload()), - dispatch("session.plugins.install", SessionPluginsInstallResult.class, """ - {"sessionId":"bound-session","source":"plugin@market"} - """, - (server, session) -> session.plugins - .install(new SessionPluginsInstallParams("foreign-session", "plugin@market"))), - dispatch("session.plugins.uninstall", Void.class, """ - {"sessionId":"bound-session","name":"plugin","directSourceId":"source-1"} - """, - (server, session) -> session.plugins - .uninstall(new SessionPluginsUninstallParams("foreign-session", "plugin", "source-1"))), - dispatch("session.plugins.update", SessionPluginsUpdateResult.class, """ - {"sessionId":"bound-session","name":"plugin@market"} - """, - (server, session) -> session.plugins - .update(new SessionPluginsUpdateParams("foreign-session", "plugin@market"))), - dispatch("session.plugins.enable", Void.class, """ - {"sessionId":"bound-session","names":["first@market","second@market"]} - """, - (server, session) -> session.plugins.enable(new SessionPluginsEnableParams("foreign-session", - List.of("first@market", "second@market")))), - dispatch("session.plugins.disable", Void.class, """ - {"sessionId":"bound-session","names":["plugin@market"]} - """, - (server, session) -> session.plugins - .disable(new SessionPluginsDisableParams("foreign-session", List.of("plugin@market")))), - dispatch("session.plugins.marketplaces.list", SessionPluginsMarketplacesListResult.class, """ - {"sessionId":"bound-session"} - """, (server, session) -> session.plugins.marketplaces.list()), - dispatch("session.plugins.marketplaces.add", SessionPluginsMarketplacesAddResult.class, """ - {"sessionId":"bound-session","source":"./market","workingDirectory":"/workspace"} - """, - (server, session) -> session.plugins.marketplaces.add( + """, (server, session) -> session.mcp.list()), + dispatch("session.mcp.listConfigured", SessionMcpListConfiguredResult.class, """ + {"sessionId":"bound-session"} + """, (server, session) -> session.mcp.listConfigured()), + dispatch("catalog.select", CatalogSelectionResult.class, """ + {"contract":{"protocolVersion":3,"requiredCapabilities":["catalog-selection"]}, + "sessionId":"catalog-session","selectionRef":"selection-1","outcome":"selected"} + """, + (server, session) -> server.catalog.select(new CatalogSelectParams( + new CatalogClientContract(3L, List.of("catalog-selection")), "catalog-session", + "selection-1", CatalogSelectionDecision.SELECTED))), + dispatch("session.managedSettings.get", SessionManagedSettingsGetResult.class, """ + {"sessionId":"bound-session"} + """, (server, session) -> session.managedSettings.get()), + dispatch("session.instructions.reload", SessionInstructionsGetSourcesResult.class, """ + {"sessionId":"bound-session"} + """, (server, session) -> session.instructions.reload()), + dispatch("session.customizations.reload", CustomizationsReloadResult.class, """ + {"sessionId":"bound-session"} + """, (server, session) -> session.customizations.reload()), + dispatch("session.plugins.install", SessionPluginsInstallResult.class, """ + {"sessionId":"bound-session","source":"plugin@market"} + """, + (server, session) -> session.plugins + .install(new SessionPluginsInstallParams("foreign-session", "plugin@market"))), + dispatch("session.plugins.uninstall", Void.class, """ + {"sessionId":"bound-session","name":"plugin","directSourceId":"source-1"} + """, + (server, session) -> session.plugins.uninstall( + new SessionPluginsUninstallParams("foreign-session", "plugin", "source-1"))), + dispatch("session.plugins.update", SessionPluginsUpdateResult.class, """ + {"sessionId":"bound-session","name":"plugin@market"} + """, + (server, session) -> session.plugins + .update(new SessionPluginsUpdateParams("foreign-session", "plugin@market"))), + dispatch("session.plugins.enable", Void.class, """ + {"sessionId":"bound-session","names":["first@market","second@market"]} + """, + (server, session) -> session.plugins.enable(new SessionPluginsEnableParams( + "foreign-session", List.of("first@market", "second@market")))), + dispatch("session.plugins.disable", Void.class, """ + {"sessionId":"bound-session","names":["plugin@market"]} + """, + (server, session) -> session.plugins.disable( + new SessionPluginsDisableParams("foreign-session", List.of("plugin@market")))), + dispatch("session.plugins.marketplaces.list", SessionPluginsMarketplacesListResult.class, """ + {"sessionId":"bound-session"} + """, (server, session) -> session.plugins.marketplaces.list()), + dispatch("session.plugins.marketplaces.add", SessionPluginsMarketplacesAddResult.class, """ + {"sessionId":"bound-session","source":"./market","workingDirectory":"/workspace"} + """, (server, session) -> session.plugins.marketplaces.add( new SessionPluginsMarketplacesAddParams("foreign-session", "./market", "/workspace"))), - dispatch("session.plugins.marketplaces.remove", SessionPluginsMarketplacesRemoveResult.class, """ - {"sessionId":"bound-session","name":"market","force":true} - """, - (server, session) -> session.plugins.marketplaces - .remove(new SessionPluginsMarketplacesRemoveParams("foreign-session", "market", true))), - dispatch("session.plugins.marketplaces.browse", SessionPluginsMarketplacesBrowseResult.class, """ - {"sessionId":"bound-session","name":"market"} - """, - (server, session) -> session.plugins.marketplaces - .browse(new SessionPluginsMarketplacesBrowseParams("foreign-session", "market"))), - dispatch("session.plugins.marketplaces.refresh", SessionPluginsMarketplacesRefreshResult.class, """ - {"sessionId":"bound-session","name":"market"} - """, - (server, session) -> session.plugins.marketplaces - .refresh(new SessionPluginsMarketplacesRefreshParams("foreign-session", "market"))), - dispatch("session.plugins.marketplaces.refresh", SessionPluginsMarketplacesRefreshResult.class, """ - {"sessionId":"bound-session"} - """, (server, session) -> session.plugins.marketplaces.refresh(null)), - dispatch("session.queue.withdrawMessage", SessionQueueWithdrawMessageResult.class, """ - {"sessionId":"bound-session","messageId":"message-1","expectedPrompt":"original"} - """, - (server, session) -> session.queue.withdrawMessage( - new SessionQueueWithdrawMessageParams("foreign-session", "message-1", "original"))), - dispatch("session.queue.appendSteering", SessionQueueAppendSteeringResult.class, """ - {"sessionId":"bound-session","messageId":"message-1","expectedPrompt":"original", - "agentMode":"interactive","prompt":"more","displayPrompt":"preview","attachments":[]} - """, (server, - session) -> session.queue.appendSteering(new SessionQueueAppendSteeringParams("foreign-session", - "message-1", "original", SendAgentMode.INTERACTIVE, "more", "preview", List.of()))), - dispatch("session.workflow.run", SessionWorkflowRunResult.class, """ - {"sessionId":"bound-session","name":"workflow-1","args":{"topic":"input"}} - """, - (server, session) -> session.workflow.run(new SessionWorkflowRunParams("foreign-session", - "workflow-1", Map.of("topic", "input"), null))), - dispatch("session.workflow.resume", SessionWorkflowResumeResult.class, """ - {"sessionId":"bound-session","runId":"run-1","notifyOnComplete":true,"logPhaseNames":false} - """, - (server, session) -> session.workflow.resume( - new SessionWorkflowResumeParams("foreign-session", "run-1", null, true, false))), - dispatch("session.workflow.runFromTool", SessionWorkflowRunFromToolResult.class, """ - {"sessionId":"bound-session","name":"workflow-1","args":{"topic":"input"},"toolCallId":"tool-1"} - """, - (server, session) -> session.workflow.runFromTool(new SessionWorkflowRunFromToolParams( - "foreign-session", "workflow-1", Map.of("topic", "input"), null, "tool-1"))), - dispatch("session.workflow.resumeFromTool", SessionWorkflowResumeFromToolResult.class, """ - {"sessionId":"bound-session","runId":"run-1","toolCallId":"tool-1"} - """, - (server, session) -> session.workflow.resumeFromTool( + dispatch("session.plugins.marketplaces.remove", SessionPluginsMarketplacesRemoveResult.class, + """ + {"sessionId":"bound-session","name":"market","force":true} + """, + (server, session) -> session.plugins.marketplaces.remove( + new SessionPluginsMarketplacesRemoveParams("foreign-session", "market", true))), + dispatch("session.plugins.marketplaces.browse", SessionPluginsMarketplacesBrowseResult.class, + """ + {"sessionId":"bound-session","name":"market"} + """, + (server, session) -> session.plugins.marketplaces.browse( + new SessionPluginsMarketplacesBrowseParams("foreign-session", "market"))), + dispatch("session.plugins.marketplaces.refresh", SessionPluginsMarketplacesRefreshResult.class, + """ + {"sessionId":"bound-session","name":"market"} + """, + (server, session) -> session.plugins.marketplaces.refresh( + new SessionPluginsMarketplacesRefreshParams("foreign-session", "market"))), + dispatch( + "session.plugins.marketplaces.refresh", SessionPluginsMarketplacesRefreshResult.class, + """ + {"sessionId":"bound-session"} + """, (server, session) -> session.plugins.marketplaces.refresh(null)), + dispatch("session.queue.withdrawMessage", SessionQueueWithdrawMessageResult.class, """ + {"sessionId":"bound-session","messageId":"message-1","expectedPrompt":"original"} + """, (server, + session) -> session.queue.withdrawMessage(new SessionQueueWithdrawMessageParams( + "foreign-session", "message-1", "original"))), + dispatch("session.queue.appendSteering", SessionQueueAppendSteeringResult.class, """ + {"sessionId":"bound-session","messageId":"message-1","expectedPrompt":"original", + "agentMode":"interactive","prompt":"more","displayPrompt":"preview","attachments":[]} + """, + (server, session) -> session.queue.appendSteering( + new SessionQueueAppendSteeringParams("foreign-session", "message-1", "original", + SendAgentMode.INTERACTIVE, "more", "preview", List.of()))), + dispatch("session.workflow.run", SessionWorkflowRunResult.class, """ + {"sessionId":"bound-session","name":"workflow-1","args":{"topic":"input"}} + """, + (server, session) -> session.workflow.run(new SessionWorkflowRunParams( + "foreign-session", "workflow-1", Map.of("topic", "input"), null))), + dispatch( + "session.workflow.resume", SessionWorkflowResumeResult.class, + """ + {"sessionId":"bound-session","runId":"run-1","notifyOnComplete":true,"logPhaseNames":false} + """, + (server, session) -> session.workflow.resume(new SessionWorkflowResumeParams( + "foreign-session", "run-1", null, true, false))), + dispatch( + "session.workflow.runFromTool", WorkflowRunResult.class, + """ + {"sessionId":"bound-session","name":"workflow-1","args":{"topic":"input"},"toolCallId":"tool-1"} + """, + (server, session) -> session.workflow.runFromTool(new SessionWorkflowRunFromToolParams( + "foreign-session", "workflow-1", Map.of("topic", "input"), null, "tool-1"))), + dispatch("session.workflow.resumeFromTool", WorkflowResumeResult.class, """ + {"sessionId":"bound-session","runId":"run-1","toolCallId":"tool-1"} + """, (server, session) -> session.workflow.resumeFromTool( new SessionWorkflowResumeFromToolParams("foreign-session", "run-1", null, "tool-1"))), - dispatch("session.workflow.getRun", SessionWorkflowGetRunResult.class, """ - {"sessionId":"bound-session","runId":"run-1"} - """, - (server, session) -> session.workflow - .getRun(new SessionWorkflowGetRunParams("foreign-session", "run-1"))), - dispatch("session.workflow.listRuns", SessionWorkflowListRunsResult.class, """ - {"sessionId":"bound-session","afterSeq":2,"limit":5} - """, - (server, session) -> session.workflow - .listRuns(new SessionWorkflowListRunsParams("foreign-session", 2L, null, 5L))), - dispatch("session.workflow.getRunDetail", SessionWorkflowGetRunDetailResult.class, """ - {"sessionId":"bound-session","runId":"run-1"} - """, - (server, session) -> session.workflow - .getRunDetail(new SessionWorkflowGetRunDetailParams("foreign-session", "run-1"))), - dispatch("session.workflow.getRunProgress", SessionWorkflowGetRunProgressResult.class, """ - {"sessionId":"bound-session","runId":"run-1","phaseId":"phase-1","afterSeq":2,"limit":5} - """, - (server, session) -> session.workflow.getRunProgress(new SessionWorkflowGetRunProgressParams( - "foreign-session", "run-1", "phase-1", 2L, null, 5L))), - dispatch("session.workflow.cancel", SessionWorkflowCancelResult.class, """ - {"sessionId":"bound-session","runId":"run-1"} - """, - (server, session) -> session.workflow - .cancel(new SessionWorkflowCancelParams("foreign-session", "run-1"))), - dispatch("session.workflow.pause", SessionWorkflowPauseResult.class, """ - {"sessionId":"bound-session","runId":"run-1"} - """, - (server, session) -> session.workflow - .pause(new SessionWorkflowPauseParams("foreign-session", "run-1"))), - dispatch("session.workflow.pauseAtCheckpoint", SessionWorkflowPauseAtCheckpointResult.class, - """ - {"sessionId":"bound-session","runId":"run-1","executionToken":"execution-1","key":"checkpoint-1"} + dispatch("session.workflow.getRun", SessionWorkflowGetRunResult.class, """ + {"sessionId":"bound-session","runId":"run-1"} + """, + (server, session) -> session.workflow + .getRun(new SessionWorkflowGetRunParams("foreign-session", "run-1"))), + dispatch("session.workflow.listRuns", SessionWorkflowListRunsResult.class, """ + {"sessionId":"bound-session","afterSeq":2,"limit":5} + """, + (server, session) -> session.workflow + .listRuns(new SessionWorkflowListRunsParams("foreign-session", 2L, null, 5L))), + dispatch("session.workflow.getRunDetail", SessionWorkflowGetRunDetailResult.class, """ + {"sessionId":"bound-session","runId":"run-1"} """, - (server, session) -> session.workflow - .pauseAtCheckpoint(new SessionWorkflowPauseAtCheckpointParams("foreign-session", - "run-1", "execution-1", "checkpoint-1"))), - dispatch("session.workflow.log", Void.class, """ - {"sessionId":"bound-session","runId":"run-1","executionToken":"execution-1", - "lines":[{"seq":1,"kind":"log","text":"progress"}]} - """, (server, - session) -> session.workflow.log(new SessionWorkflowLogParams("foreign-session", "run-1", - "execution-1", List.of(new WorkflowLogLine(1L, WorkflowLogLineKind.LOG, "progress"))))), - dispatch("session.workflow.agent", SessionWorkflowAgentResult.class, - """ - {"sessionId":"bound-session","workflowRunId":"run-1","executionToken":"execution-1","prompt":"inspect"} + (server, session) -> session.workflow.getRunDetail( + new SessionWorkflowGetRunDetailParams("foreign-session", "run-1"))), + dispatch("session.workflow.getRunProgress", SessionWorkflowGetRunProgressResult.class, """ + {"sessionId":"bound-session","runId":"run-1","phaseId":"phase-1","afterSeq":2,"limit":5} + """, (server, + session) -> session.workflow.getRunProgress(new SessionWorkflowGetRunProgressParams( + "foreign-session", "run-1", "phase-1", 2L, null, 5L))), + dispatch("session.workflow.cancel", SessionWorkflowCancelResult.class, """ + {"sessionId":"bound-session","runId":"run-1"} """, - (server, session) -> session.workflow.agent(new SessionWorkflowAgentParams("foreign-session", - "run-1", "execution-1", "inspect", null))), - dispatch("session.workflow.journal.get", SessionWorkflowJournalGetResult.class, """ - {"sessionId":"bound-session","runId":"run-1","executionToken":"execution-1","key":"journal-1"} - """, - (server, session) -> session.workflow.journal.get(new SessionWorkflowJournalGetParams( - "foreign-session", "run-1", "execution-1", "journal-1"))), - dispatch("session.workflow.journal.put", Void.class, """ - {"sessionId":"bound-session","runId":"run-1","executionToken":"execution-1","key":"journal-1", - "resultJson":{"answer":42}} - """, (server, session) -> session.workflow.journal.put(new SessionWorkflowJournalPutParams( - "foreign-session", "run-1", "execution-1", "journal-1", Map.of("answer", 42))))); + (server, session) -> session.workflow + .cancel(new SessionWorkflowCancelParams("foreign-session", "run-1"))), + dispatch("session.workflow.pause", SessionWorkflowPauseResult.class, """ + {"sessionId":"bound-session","runId":"run-1"} + """, + (server, session) -> session.workflow + .pause(new SessionWorkflowPauseParams("foreign-session", "run-1"))), + dispatch( + "session.workflow.pauseAtCheckpoint", WorkflowPauseCheckpointResult.class, + """ + {"sessionId":"bound-session","runId":"run-1","executionToken":"execution-1","key":"checkpoint-1"} + """, + (server, session) -> session.workflow + .pauseAtCheckpoint(new SessionWorkflowPauseAtCheckpointParams("foreign-session", + "run-1", "execution-1", "checkpoint-1"))), + dispatch("session.workflow.log", Void.class, """ + {"sessionId":"bound-session","runId":"run-1","executionToken":"execution-1", + "lines":[{"seq":1,"kind":"log","text":"progress"}]} + """, + (server, session) -> session.workflow.log(new SessionWorkflowLogParams( + "foreign-session", "run-1", "execution-1", + List.of(new WorkflowLogLine(1L, WorkflowLogLineKind.LOG, "progress"))))), + dispatch("session.workflow.agent", SessionWorkflowAgentResult.class, + """ + {"sessionId":"bound-session","workflowRunId":"run-1","executionToken":"execution-1","prompt":"inspect"} + """, + (server, session) -> session.workflow.agent(new SessionWorkflowAgentParams( + "foreign-session", "run-1", "execution-1", "inspect", null))), + dispatch("session.workflow.journal.get", SessionWorkflowJournalGetResult.class, + """ + {"sessionId":"bound-session","runId":"run-1","executionToken":"execution-1","key":"journal-1"} + """, + (server, session) -> session.workflow.journal.get(new SessionWorkflowJournalGetParams( + "foreign-session", "run-1", "execution-1", "journal-1"))), + dispatch("session.workflow.journal.put", Void.class, + """ + {"sessionId":"bound-session","runId":"run-1","executionToken":"execution-1","key":"journal-1", + "resultJson":{"answer":42}} + """, + (server, session) -> session.workflow.journal + .put(new SessionWorkflowJournalPutParams("foreign-session", "run-1", + "execution-1", "journal-1", Map.of("answer", 42))))); } private static Arguments dispatch(String method, Class resultType, String expectedParams, @@ -371,7 +391,7 @@ void sessionRpc_mode_set_merges_sessionId() { var session = new SessionRpc(stub, "sess-mode-set"); var modeParams = new SessionModeSetParams(null, null, null, null, null, null, null, null, null, null, null, - null, null); + null, null, null); session.mode.set(modeParams); assertEquals(1, stub.calls.size()); @@ -551,8 +571,9 @@ void sessionRpc_mcp_list_injects_sessionId() { assertEquals(1, stub.calls.size()); assertEquals("session.mcp.list", stub.calls.get(0).method()); - var params = (Map) stub.calls.get(0).params(); - assertEquals("sess-mcp", params.get("sessionId")); + var mapper = new ObjectMapper(); + assertEquals(mapper.valueToTree(Map.of("sessionId", "sess-mcp")), + mapper.valueToTree(stub.calls.get(0).params())); } @Test 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 51fbaa865c..348a2d9f1a 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 @@ -19,6 +19,7 @@ import org.junit.jupiter.params.provider.MethodSource; import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.AllowCopilotExperimental; import com.github.copilot.TestUtil; /** @@ -222,6 +223,52 @@ void sessionFsSetProviderParams_conventions_enum() { assertThrows(IllegalArgumentException.class, () -> SessionFsSetProviderConventions.fromValue("unknown")); } + @Test + void sessionFsSetProviderCapabilities_preserves_legacy_constructor_and_binary_wire_field() throws Exception { + var mapper = new ObjectMapper(); + var legacy = new SessionFsSetProviderCapabilities(true); + var legacyJson = mapper.readTree(mapper.writeValueAsString(legacy)); + assertTrue(legacyJson.get("sqlite").asBoolean()); + assertFalse(legacyJson.has("binary")); + assertEquals(legacy, mapper.treeToValue(legacyJson, SessionFsSetProviderCapabilities.class)); + + var binary = new SessionFsSetProviderCapabilities(false, true); + var binaryJson = mapper.readTree(mapper.writeValueAsString(binary)); + assertFalse(binaryJson.get("sqlite").asBoolean()); + assertTrue(binaryJson.get("binary").asBoolean()); + assertEquals(binary, mapper.treeToValue(binaryJson, SessionFsSetProviderCapabilities.class)); + } + + @Test + @AllowCopilotExperimental + void sessionFsReadFileBytes_preserves_base64_wire_content() throws Exception { + var mapper = new ObjectMapper(); + var params = new SessionFsReadFileBytesParams("sess-15", "/image.png"); + var paramsJson = mapper.readTree(mapper.writeValueAsString(params)); + assertEquals("sess-15", paramsJson.get("sessionId").asText()); + assertEquals("/image.png", paramsJson.get("path").asText()); + assertEquals(params, mapper.treeToValue(paramsJson, SessionFsReadFileBytesParams.class)); + + var result = new SessionFsReadFileBytesResult("AAECAw==", null); + var resultJson = mapper.readTree(mapper.writeValueAsString(result)); + assertEquals("AAECAw==", resultJson.get("content").asText()); + assertFalse(resultJson.has("error")); + assertEquals(result, mapper.treeToValue(resultJson, SessionFsReadFileBytesResult.class)); + } + + @Test + @AllowCopilotExperimental + void sessionFsWriteFileBytes_preserves_base64_wire_content_and_mode() throws Exception { + var mapper = new ObjectMapper(); + var params = new SessionFsWriteFileBytesParams("sess-15", "/image.png", "AP/+AQ==", 384L); + var json = mapper.readTree(mapper.writeValueAsString(params)); + assertEquals("sess-15", json.get("sessionId").asText()); + assertEquals("/image.png", json.get("path").asText()); + assertEquals("AP/+AQ==", json.get("content").asText()); + assertEquals(384L, json.get("mode").asLong()); + assertEquals(params, mapper.treeToValue(json, SessionFsWriteFileBytesParams.class)); + } + @Test void sessionFsStatParams_record() { var params = new SessionFsStatParams("sess-20", "/etc/hosts"); @@ -287,12 +334,6 @@ void sessionMcpEnableParams_record() { assertEquals("mcp-server-2", params.serverName()); } - @Test - void sessionMcpListParams_record() { - var params = new SessionMcpListParams("sess-27"); - assertEquals("sess-27", params.sessionId()); - } - @Test void sessionMcpReloadParams_record() { var params = new SessionMcpReloadParams("sess-28"); @@ -308,7 +349,7 @@ void sessionModeGetParams_record() { @Test void sessionModeSetParams_record() { var params = new SessionModeSetParams("sess-30", SessionMode.PLAN, null, null, null, null, null, null, null, - null, null, null, null); + null, null, null, null, null); assertEquals("sess-30", params.sessionId()); assertEquals(SessionMode.PLAN, params.mode()); } @@ -428,6 +469,7 @@ void sessionModelSwitchParams_distinguishRequiredNullFromOmittedOptionalValue() var switchTo = mapper.valueToTree(new SessionModelSwitchToParams("sess-32", "auto", null, null, null, null, null, null, null, null, null, null, null, null, null, null)); assertFalse(switchTo.has("autoTier")); + assertFalse(switchTo.has("providerId")); } @Test @@ -850,8 +892,10 @@ void sessionMcpListResult_status_enum_all_values() { @Test void sessionModelGetCurrentResult_record() { - var result = new SessionModelGetCurrentResult("claude-sonnet-5", null, null, null, null, null); + var result = new SessionModelGetCurrentResult("claude-sonnet-5", null, "claude-sonnet-4.5", null, null, null, + null, null, null); assertEquals("claude-sonnet-5", result.modelId()); + assertEquals("claude-sonnet-4.5", result.planBaseModelId()); } @Test @@ -960,8 +1004,8 @@ void sessionUiHandlePendingElicitationResult_record() { @Test void sessionUsageGetMetricsResult_nested() { var changes = new UsageMetricsCodeChanges(100L, 50L, 5L, null); - var result = new SessionUsageGetMetricsResult(0.5, 10L, null, null, 2000L, null, changes, null, null, "gpt-5", - 1000L, 500L); + var result = new SessionUsageGetMetricsResult(null, 0.5, 10L, null, null, 2000L, null, changes, null, null, + null, "gpt-5", 1000L, 500L); assertEquals(0.5, result.totalPremiumRequestCost()); assertEquals(10L, result.totalUserRequests()); assertNotNull(result.codeChanges()); @@ -1015,19 +1059,20 @@ void mcpDiscoverResult_nested() { @Test void modelsListResult_nested() { - var supports = new ModelCapabilitiesSupports(true, null, false, null); + var supports = new ModelCapabilitiesSupports(false, true, null, false, null); var limits = new ModelCapabilitiesLimits(100000L, 8192L, 128000L, null); var capabilities = new ModelCapabilities(supports, limits); var policy = new ModelPolicy(ModelPolicyState.ENABLED, null); var promo = new ModelBillingPromo("summer-2026", 25.0, "2026-08-01T00:00:00Z", "Summer discount", true); var billing = new ModelBilling(1.0, null, null, promo); - var modelItem = new Model("gpt-5", "GPT-5", capabilities, null, policy, billing, null, null, null, null, null, - null, null, null, null); + var modelItem = new Model("gpt-5", "GPT-5", capabilities, null, null, policy, billing, null, null, null, null, + null, null, null, null, null); var result = new ModelsListResult(List.of(modelItem)); assertEquals(1, result.models().size()); assertEquals("gpt-5", result.models().get(0).id()); assertEquals("GPT-5", result.models().get(0).name()); + assertFalse(result.models().get(0).capabilities().supports().thinking()); assertTrue(result.models().get(0).capabilities().supports().vision()); assertFalse(result.models().get(0).capabilities().supports().reasoningEffort()); assertEquals(100000L, result.models().get(0).capabilities().limits().maxPromptTokens()); diff --git a/java/sdk/src/test/java/com/github/copilot/generated/rpc/McpPromptsTest.java b/java/sdk/src/test/java/com/github/copilot/generated/rpc/McpPromptsTest.java new file mode 100644 index 0000000000..4c0eb8050f --- /dev/null +++ b/java/sdk/src/test/java/com/github/copilot/generated/rpc/McpPromptsTest.java @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.generated.rpc; + +import static org.junit.jupiter.api.Assertions.*; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * Generated dispatch and serialization supplement TypeScript's real MCP tests. + */ +class McpPromptsTest { + private final ObjectMapper mapper = new ObjectMapper(); + + @Test + void promptResultsPreserveOpaqueContentAndOptionalFields() throws Exception { + Path directory = Path.of("").toAbsolutePath(); + Path fixture = null; + while (directory != null) { + Path candidate = directory.resolve("test/harness/mcp-prompt-fixtures.json"); + if (Files.exists(candidate)) { + fixture = candidate; + break; + } + directory = directory.getParent(); + } + assertNotNull(fixture, "Cannot find shared MCP prompt fixture"); + JsonNode fixtures = mapper.readTree(fixture.toFile()); + for (String page : List.of("firstPage", "secondPage")) { + var result = mapper.treeToValue(fixtures.get(page), SessionMcpPromptsListResult.class); + assertEquals(fixtures.get(page), mapper.valueToTree(result)); + } + var result = mapper.treeToValue(fixtures.get("richPrompt"), SessionMcpPromptsGetResult.class); + assertEquals(fixtures.get("richPrompt"), mapper.valueToTree(result)); + } + + @Test + void promptNamespaceDispatchesBoundSessionAndPreservesOptionalArguments() throws Exception { + record Call(String method, JsonNode params, Class resultType) { + } + List calls = new ArrayList<>(); + RpcCaller caller = new RpcCaller() { + @Override + public CompletableFuture invoke(String method, Object params, Class resultType) { + calls.add(new Call(method, mapper.valueToTree(params), resultType)); + return CompletableFuture.completedFuture(null); + } + }; + var session = new SessionRpc(caller, "bound-session"); + session.mcp.prompts.list(new SessionMcpPromptsListParams("foreign-session", "fixture", null)).join(); + session.mcp.prompts.list(new SessionMcpPromptsListParams("foreign-session", "fixture", "page:2/opaque+cursor=")) + .join(); + session.mcp.prompts.get(new SessionMcpPromptsGetParams("foreign-session", "fixture", "rich", null)).join(); + session.mcp.prompts.get(new SessionMcpPromptsGetParams("foreign-session", "fixture", "rich", Map.of())).join(); + session.mcp.prompts.get(new SessionMcpPromptsGetParams("foreign-session", "fixture", "rich", + Map.of("topic", "日本語", "style", ""))).join(); + + assertEquals(5, calls.size()); + for (int i = 0; i < calls.size(); i++) { + Call call = calls.get(i); + assertEquals("bound-session", call.params().get("sessionId").asText()); + assertEquals("fixture", call.params().get("serverName").asText()); + assertEquals(i < 2 ? "session.mcp.prompts.list" : "session.mcp.prompts.get", call.method()); + assertEquals(i < 2 ? SessionMcpPromptsListResult.class : SessionMcpPromptsGetResult.class, + call.resultType()); + } + assertFalse(calls.get(0).params().has("cursor")); + assertEquals("page:2/opaque+cursor=", calls.get(1).params().get("cursor").asText()); + assertEquals("rich", calls.get(2).params().get("promptName").asText()); + assertFalse(calls.get(2).params().has("arguments")); + assertEquals(mapper.readTree("{}"), calls.get(3).params().get("arguments")); + assertEquals(mapper.readTree("{\"topic\":\"日本語\",\"style\":\"\"}"), calls.get(4).params().get("arguments")); + } +} diff --git a/java/sdk/src/test/prompts/PROMPT-smoke-test.md b/java/sdk/src/test/prompts/PROMPT-smoke-test.md deleted file mode 100644 index 4013002aca..0000000000 --- a/java/sdk/src/test/prompts/PROMPT-smoke-test.md +++ /dev/null @@ -1,135 +0,0 @@ -# Prompt: Generate and Run the copilot-sdk-java Smoke Test - -## Objective - -Create a Maven project that acts as a smoke test for `copilot-sdk-java`. The project must compile, build, and run to completion with exit code 0 as the definition of success. - -## Step 1 — Read the source README - -Read the file `README.md` at the top level of this repository. You will need two sections from it: - -- **"Snapshot Builds"** — provides the Maven GAV (groupId, artifactId, version) and the Maven Central Snapshots repository configuration to use for the dependency under test. -- **"Quick Start"** — provides the exact Java source code for the smoke test program. Use this code verbatim. Do not modify it, fix it, or improve it. If it does not compile or run correctly against the artifact under test, that is itself a smoke test failure and must be reported as such rather than silently corrected. - -## Step 2 — Create the Maven project - -Create the following file layout in a subdirectory named `smoke-test/` at the top level of this repository: - -``` -smoke-test/ - pom.xml - src/main/java/(Class name taken from the code in the "Quick Start" section in the README).java ← verbatim from README "Quick Start" -``` - -### `pom.xml` requirements - -- **groupId**: `com.github` (or any reasonable value) -- **artifactId**: `copilot-sdk-smoketest` -- **version**: `1.0-SNAPSHOT` -- **packaging**: `jar` -- **Java source/target**: (taken from the "Requirements" section in the README) (via `maven.compiler.source` and `maven.compiler.target` properties) -- **`mainClass` property**: (taken from the "Quick Start" section in the README) (the class is in the default package) - -#### Snapshot repository - -Configure the Maven Central Snapshots repository exactly as specified in the "Snapshot Builds" section of `README.md`, and add `always` inside the `` block so that every build fetches the latest snapshot without requiring `-U`: - -```xml - - central-snapshots - https://central.sonatype.com/repository/maven-snapshots/ - - true - always - - -``` - -#### Dependency - -Use the GAV from the "Snapshot Builds" section of `README.md` verbatim — do not substitute the release version from the "Maven" section. - -#### Plugins — REQUIRED configuration - -**Do not use `maven-shade-plugin`.** Use the `Class-Path` manifest approach instead: - -1. **`maven-jar-plugin`** (version **3.4.1** — pin explicitly to suppress Maven version warnings): - - ```xml - - org.apache.maven.plugins - maven-jar-plugin - 3.4.1 - - - - ${mainClass} - true - lib/ - false - - - - - ``` - - **Critical**: `false` is mandatory. Without it, the manifest `Class-Path:` entry uses the timestamped SNAPSHOT filename (e.g. `copilot-sdk-java-0.1.33-20260312.125508-3.jar`) while `copy-dependencies` writes the base SNAPSHOT filename (`copilot-sdk-java-0.1.33-SNAPSHOT.jar`), causing `NoClassDefFoundError` at runtime. - -2. **`maven-dependency-plugin`** (version **3.6.1**): - - ```xml - - org.apache.maven.plugins - maven-dependency-plugin - 3.6.1 - - - copy-dependencies - package - copy-dependencies - - ${project.build.directory}/lib - - - - - ``` - - This copies all runtime dependency JARs into `target/lib/`, which is where the manifest `Class-Path:` points. - -## Step 3 — Build - -```bash -mvn -U clean package -``` - -The `-U` flag forces a fresh snapshot metadata check regardless of local cache. The `always` already handles this for normal invocations, but `-U` is the safest choice for CI. - -Build must succeed with `BUILD SUCCESS` before proceeding. - -## Step 4 — Run - -```bash -java -jar ./target/copilot-sdk-smoketest-1.0-SNAPSHOT.jar -``` - -The JAR must be run from the `smoke-test/` directory so that the relative `lib/` path in the manifest resolves correctly. Do not use `-cp` or `-classpath` — the test specifically validates that `java -jar` works with the manifest `Class-Path:` approach. - -## Step 5 — Verify success - -The smoke test passes if and only if the process exits with code **0**. - -The "Quick Start" code in `README.md` already contains the exit-code logic: it captures the last assistant message and calls `System.exit(0)` if it contains `"4"` (the expected answer to "What is 2+2?"), or `System.exit(-1)` otherwise. - -Check the exit code: -```bash -echo "Exit code: $?" -``` - -Expected: `Exit code: 0` - -## Important API notes (do not apply these as fixes — they are here for diagnostic context only) - -If the build fails with compilation errors such as `cannot find symbol` on methods like `getContent()`, `getCurrentTokens()`, `getTokenLimit()`, or `getMessagesLength()`, this indicates a mismatch between the Quick Start code and the SDK implementation. **Do not silently fix the code.** Report the failure. The purpose of this smoke test is precisely to catch such regressions. - -For reference: the data classes in `copilot-sdk-java` are Java **records**. Record accessor methods have no `get` prefix — they are named `content()`, `currentTokens()`, `tokenLimit()`, and `messagesLength()`. If the README Quick Start uses `getContent()` etc., that is a bug in the README that must be surfaced, not silently corrected. diff --git a/nodejs/README.md b/nodejs/README.md index 59e15ea274..1566ea9cad 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -74,6 +74,9 @@ without deleting underlying sessions. Reconnecting does not reclaim a host. The optional `onExit` callback reports exits at most once. If the owner connection is lost, it reports that loss rather than claiming that listener cleanup was acknowledged. +Call `const { sessions } = await host.listSessions()` to read the complete catalog +of live and dormant sessions advertised by the host. The method returns a +`HostListSessionsResult` containing `sessions`, not a session array directly. `host.pid` is absent for in-process listeners. The optional field is retained for separate host process IDs returned by legacy runtimes, never the runtime PID. @@ -104,6 +107,10 @@ The listener follows the owning client's lifetime. Call `await host.dispose()` only when you want to stop it earlier; `await using host` also supports a shorter scope. Each disposal call forwards to the runtime, which owns idempotent cleanup. The AHP transport remains owned by the host. +Set top-level `computeId` to reuse the same durable catalog for local and Mission +Control hosting; it must agree with `githubEnvironment.computeId` when both are +supplied. Omitted local-only identities are persisted by the runtime, not generated +by the SDK. Published sessions remain discoverable after hosting stops. ### Application-owned sessions @@ -165,8 +172,9 @@ still matches the requested identity and workspace. Only catalog entries marked as application-owned invoke this callback. If the callback is missing, restoring such an entry fails instead of falling back to host-owned creation. -Published resident sessions attach directly, without invoking it or replacing -their current registrations. Resumed sessions follow the same original-object +Currently resident published sessions attach directly, without invoking it or replacing +their current registrations. Dormant published application sessions require this +resume callback to restore their tools and handlers. Resumed sessions follow the same original-object retention, cancellation, late-result release, and `onSessionReleased` rules. The `copilotd-hosting` library runs inside the runtime provider. @@ -270,6 +278,19 @@ new CopilotClient(options?: CopilotClientOptions) - `telemetry?: TelemetryConfig` - OpenTelemetry configuration for the runtime process. Providing this object enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below. - `onGetTraceContext?: TraceContextProvider` - Advanced: callback for linking your application's own OpenTelemetry spans into the same distributed trace as the runtime's spans. Not needed for normal telemetry collection. See [Telemetry](#telemetry) below. - `sessionFs?: SessionFsConfig` - Custom session filesystem provider. + +For a custom provider to serve images to the `view` tool, declare +`sessionFs.capabilities.binary: true` and implement +`readFileBytes(path): Promise` and +`writeFileBytes(path, content: Uint8Array, mode?: number): Promise` on each session's provider. The +adapter transmits exact bytes as standard base64; text-only providers still +support text operations, but image reads fail rather than falling back to the +runtime machine's filesystem. A missing binary implementation is rejected +when a session is created. + +Binary reads and writes are limited to 50,330,880 raw bytes (approximately 48 MiB); +larger results return a filesystem error before encoding or decoding. + - `sessionIdleTimeoutSeconds?: number` - Server-wide idle timeout for sessions in seconds. Ignored when connecting via `RuntimeConnection.forUri`. - `enableRemoteSessions?: boolean` - Enable Mission Control remote session support. Ignored when connecting via `RuntimeConnection.forUri`. @@ -332,6 +353,11 @@ This graceful-exit timeout is separate from the shutdown RPC and post-terminatio ##### `forceStop(): Promise` Force stop the CLI server without graceful cleanup. Use when `stop()` takes too long. +Retained sessions are disconnected locally, and pending `sendAndWait()` calls reject +without waiting for `session.idle` or their response timeout. +Retirement releases session-owned tool, hook, auth/input, provider, workflow, canvas, +and transform registrations without changing caller-owned configuration. Late +registration and callback dispatch are rejected; callbacks already admitted may finish. ##### `createSession(config?: SessionConfig): Promise` @@ -356,6 +382,7 @@ Create a new conversation session. - `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`. - `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). ```typescript const session = await client.createSession({ @@ -644,6 +671,24 @@ if (result.status === "pending") { See [Auto tier persistence](../docs/features/session-persistence.md#auto-tier-persistence) for the full lifecycle rules. +##### `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. + +Once the runtime accepts the replacement, every tool call this session dispatches uses the new handlers; calls already running finish on their original handlers. If the runtime rejects it, the promise rejects and the previous tools and handlers stay in place. Concurrent calls are applied one at a time, in call order. + +```typescript +await session.setTools([ + defineTool("search_issues", { + description: "Search the issues shown on the current page", + parameters: z.object({ query: z.string() }), + handler: async ({ query }) => searchIssues(query), + }), +]); +``` + +The agent sees the new tools from its next model request, which can fall within a turn in progress, so a model request already in flight can still call a tool you removed. See [Changing tools during a session](../docs/features/changing-tools.md) for the details. + ##### `abort(): Promise` Abort the currently processing message in this session. @@ -839,6 +884,22 @@ defineTool("edit_file", { }); ``` +An `apply_patch` override can declare a string schema. The model sees a required +`input` property, but the runtime restores the declared scalar shape before +dispatching to any SDK. Both a Zod-inferred Node handler and +`invocation.arguments` receive the patch text as a string: + +```ts +defineTool("apply_patch", { + parameters: z.string(), + overridesBuiltInTool: true, + handler: (patch) => patch.trim(), +}); +``` + +String-schema `apply_patch` overrides cannot contain JSON Schema references; +use an object schema if references are needed. + #### Skipping Permission Prompts Set `skipPermission: true` on a tool definition to allow it to execute without triggering a permission prompt: @@ -988,6 +1049,8 @@ Available section IDs: `preamble`, `identity`, `tone`, `tool_efficiency`, `envir `identity` and `tool_instructions` are section _groups_ that target a collection of related sub-sections as a unit. Use `preamble` to target just the identity preamble without affecting its sibling sub-sections. +`last_instructions` includes configured subagent-model guidance when the `task` tool is available. Removing or replacing this section also removes that guidance; a transform callback receives the complete section, including the guidance, and its returned content is authoritative. Append, prepend, and preserve retain their usual section semantics. These overrides change prompt prose only, not configured subagent models, tool availability, or runtime dispatch policy. `runtime_instructions` is a separate section: removing it does not remove `last_instructions`. + Each section override supports five actions: - **`replace`** — Replace the section content entirely @@ -996,6 +1059,8 @@ Each section override supports five actions: - **`prepend`** — Add content before the existing section - **`preserve`** — No-op that opts an individually-addressable section out of a group-level `remove` +An `action` can also be a callback that receives the current section content and returns the replacement content, synchronously or asynchronously. + Unknown section IDs are handled gracefully: content from `replace`/`append`/`prepend` overrides is appended to additional instructions, and `remove` overrides are silently ignored. #### Replace Mode @@ -1069,6 +1134,46 @@ const session = await client.createSession({ }); ``` +### Skill providers (experimental) + +A skill provider serves skills from your own storage, such as a database, instead of `SKILL.md` +files on disk. Provider skills join the session's skill catalog and load on demand through the +`skill` tool, just like file-based skills. + +```typescript +import type { SkillProvider } from "@github/copilot-sdk"; + +const skillProvider: SkillProvider = { + listSkills: async () => [ + { name: "release-notes", description: "Writes release notes in the team's format." }, + ], + readSkill: async (name, { signal }) => (await db.findSkill(name, { signal }))?.markdown ?? null, +}; + +const session = await client.createSession({ + onPermissionRequest: approveAll, + skillProvider, +}); +``` + +- `listSkills()` returns the catalog metadata. `readSkill(name)` returns the skill's markdown, or + `null`/`undefined` if the skill no longer exists. The markdown may omit YAML frontmatter; when + frontmatter is present, its fields must agree with the listed metadata, and `allowed-tools` is + read only from frontmatter. +- Each call receives `{ signal }`, an `AbortSignal` that fires when the runtime cancels the call, + for example after its 30-second limit or when the session disconnects. It doesn't fire when the + connection closes or the client is force-stopped; a running call then continues until it returns. +- The provider is never persisted. Pass it again to `resumeSession`; resuming without it unbinds + the provider. +- A provider enables skills unless you set `enableSkills: false`, which keeps it bound but unused. + In `mode: "empty"`, skills stay disabled until you set `enableSkills: true`. +- Errors thrown by the provider are reported to the model as a generic load failure; their + messages are not forwarded. +- The runtime may call the provider concurrently, so both methods must be safe for concurrent use. +- Skill providers are not supported for cloud sessions. + +See [Custom skills](../docs/features/skills.md#skill-providers-experimental) for limits and details. + ### Multiple Sessions ```typescript @@ -1443,6 +1548,20 @@ const session = await client.createSession({ }; } }, + + // Called before a sub-agent's first turn (the input identifies the parent session) + onSubagentStart: (input, invocation) => { + console.log(`Starting ${input.agentDisplayName ?? input.agentName}`); + return { additionalContext: "Check the requested file before reporting back." }; + }, + + // Called when a sub-agent completes a turn + onSubagentStop: (input, invocation) => { + console.log(`${input.agentName} replied: ${input.response}`); + // Return { decision: "block", reason: "Continue checking the file." } + // to request another child turn instead. + return { modifiedResponse: `Reviewed: ${input.response}` }; + }, }, }); ``` @@ -1457,6 +1576,8 @@ const session = await client.createSession({ - `onSessionEnd` - Cleanup or logging when session ends. - `onErrorOccurred` - Handle errors with retry/skip/abort strategies. - `onAgentStop` - Observe natural top-level agent completion. Return `{ decision: "block", reason }` to request another turn; use `stopHookActive` to avoid repeated blocks. +- `onSubagentStart` - Observe a sub-agent before its first turn and prepend `additionalContext` to the child's prompt. +- `onSubagentStop` - Observe a sub-agent's final response. Return `{ decision: "block", reason }` to request another child turn, or `{ modifiedResponse }` to replace the response reported to the parent. ## Error Handling diff --git a/nodejs/docs/agent-author.md b/nodejs/docs/agent-author.md index 6b9366a7e6..73875cea14 100644 --- a/nodejs/docs/agent-author.md +++ b/nodejs/docs/agent-author.md @@ -122,10 +122,12 @@ hooks: { onSessionStart: async (input, invocation) => { ... }, onSessionEnd: async (input, invocation) => { ... }, onErrorOccurred: async (input, invocation) => { ... }, + onSubagentStart: async (input, invocation) => { ... }, + onSubagentStop: async (input, invocation) => { ... }, } ``` -All hook inputs include `timestamp` (`Date`) and `workingDirectory`. +All hook inputs include `sessionId`, `timestamp` (`Date`) and `workingDirectory`. All handlers receive `invocation: { sessionId: string }` as the second argument. All handlers may return `void`/`undefined` (no-op) or an output object. @@ -214,7 +216,32 @@ fire it. | `retryCount` | `number` | Max retries (when errorHandling is "retry") | | `userNotification` | `string` | Message shown to the user | ---- +### onSubagentStart + +Fires before a sub-agent's first turn. The input's `sessionId` and the +invocation's `sessionId` identify the parent session, not the child. + +**Input:** `{ sessionId: string, transcriptPath: string, agentName: string, agentDisplayName?: string, agentDescription?: string, timestamp, workingDirectory }` + +**Output (optional):** +| Field | Type | Effect | +|-------|------|--------| +| `additionalContext` | `string` | Prepended to the child's initial prompt | + +### onSubagentStop + +Fires after a sub-agent completes a turn. The input includes the child's last +assistant `response` and the parent's session metadata. `agentId` is available +when the task registry supplies one. This is distinct from `onAgentStop`, which +only runs for the top-level agent. + +**Input:** `{ sessionId: string, transcriptPath: string, agentName: string, agentDisplayName?: string, agentDescription?: string, agentId?: string, agentType: string, stopReason: "end_turn", response: string, timestamp, workingDirectory }` + +**Output (choose one, or return nothing):** +| Field | Type | Effect | +|-------|------|--------| +| `decision` and `reason` | `"block"` and `string` | Continue the child for another turn using `reason` | +| `modifiedResponse` | `string` | Replace the child's response reported to the parent | ## Session Object diff --git a/nodejs/docs/workflows.md b/nodejs/docs/workflows.md index 872cbcb0fe..7f2985f614 100644 --- a/nodejs/docs/workflows.md +++ b/nodejs/docs/workflows.md @@ -186,7 +186,7 @@ session.workflow.resume( Set `notifyOnComplete` to `true` for workflows that are likely to be invoked by an agent, so the originating session is notified when the workflow completes. Set it to `false` for workflows intended to be invoked programmatically, where the caller awaits the result directly. Set `logPhaseNames` to emit workflow phase names to the session transcript. Both options apply to new and resumed runs. -Both resolve with the run envelope (`WorkflowRunResult`) for **every** outcome—`completed`, `error`, `halted`, `paused`, and `cancelled` alike. Inspect `status` and read `result` only when the run completed; a limit breach carries a typed `failure`. A `paused` envelope means that the current attempt settled, not that the durable run is permanently finished. Resume the same run ID to start another attempt with its journal and accounting intact. SDK-initiated `run` and `resume` do not request permission, so they have no declined outcome. An SDK-initiated run is refused only when the session already has its maximum number of active top-level runs. Pre-execution resume failures throw `WorkflowResumeError`, whose `code` is one of `not_found`, `non_resumable`, `workflow_run_not_resumable`, `already_active`, `workflow_already_running`, `workflow_limits_invalid`, `workflow_session_disposed`, `workflow_storage_unavailable`, or `workflow_storage_corrupt`. +Both resolve with the run envelope (`WorkflowRunResult`) for **every** outcome—`completed`, `error`, `halted`, `paused`, and `cancelled` alike. Inspect `status` and read `result` only when the run completed; a limit breach carries a typed `failure`. A `paused` envelope means that the current attempt settled, not that the durable run is permanently finished. Resume the same run ID to start another attempt with its journal and accounting intact. SDK-initiated `run` and `resume` do not request permission, so they have no declined outcome. They still enforce execution eligibility, input validation, and active-run limits. Recognized pre-execution resume failures throw `WorkflowResumeError`, whose `code` is one of `not_found`, `non_resumable`, `workflow_run_not_resumable`, `already_active`, `workflow_already_running`, `workflow_limits_invalid`, `workflow_session_disposed`, `workflow_storage_unavailable`, or `workflow_storage_corrupt`. Pause a running attempt from outside its workflow body: @@ -206,7 +206,13 @@ The first attempt pauses at `"review-ready"` and ends through cooperative cancel ## Observe a run -The calling session can inspect its own workflow runs: +The calling session can inspect its own workflow runs independently of its current execution eligibility. An empty registry enumerates successfully; losing eligibility does not hide already-admitted runs or terminal history. Observation still requires a valid, non-disposed session, and validation, missing-run, and storage errors still propagate. + +Workflow storage requires SQLite support from a custom session filesystem. A provider without that capability reports `workflow_storage_unavailable`, not an empty registry or a local-storage fallback. + +Compaction waits at most five seconds for its optional active-workflow summary. If storage fails or times out, compaction logs a warning and continues without that summary; aborting compaction cancels the wait. This does not change the results or error handling of explicit workflow observation calls. + +Observation does not restore execution eligibility. Starting, resuming, pausing, or cancelling runs, and workflow-owned agent, log, and journal/checkpoint operations still require eligible credentials (token-based billing or trusted HMAC authentication). Ineligible execution requests fail with JSON-RPC `-32601` and `data.code: "dynamic_workflows_unavailable"`. ```ts const runs = await session.workflow.listRuns(); diff --git a/nodejs/esbuild-copilotsdk-nodejs.ts b/nodejs/esbuild-copilotsdk-nodejs.ts index f65a47236f..d3c5030a4e 100644 --- a/nodejs/esbuild-copilotsdk-nodejs.ts +++ b/nodejs/esbuild-copilotsdk-nodejs.ts @@ -34,4 +34,4 @@ import { writeFileSync } from "fs"; writeFileSync("dist/cjs/package.json", JSON.stringify({ type: "commonjs" }) + "\n"); // Generate .d.ts files -execSync("tsc", { stdio: "inherit" }); +execSync("tsc --incremental --tsBuildInfoFile dist/tsconfig.tsbuildinfo", { stdio: "inherit" }); diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index 937a26efa1..e8104e0779 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -24,6 +24,7 @@ "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", @@ -665,6 +666,63 @@ "dev": true, "license": "MIT" }, + "node_modules/@hpke/chacha20poly1305": { + "version": "1.8.0", + "integrity": "sha1-1NL248zgi0xVU5IOFbTlmMildy4=", + "dev": true, + "license": "MIT", + "dependencies": { + "@hpke/common": "^1.10.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@hpke/common": { + "version": "1.10.1", + "integrity": "sha1-EfIF5bok1VjBvUrGcVgNldSIrxg=", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@hpke/core": { + "version": "1.9.0", + "integrity": "sha1-Ts7QWXeH9R/bU61RXD1Ho6vJ/jo=", + "dev": true, + "license": "MIT", + "dependencies": { + "@hpke/common": "^1.10.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@hpke/dhkem-x25519": { + "version": "1.8.0", + "integrity": "sha1-S3lerZNjhH6CiEuQDmUBgUPYuZo=", + "dev": true, + "license": "MIT", + "dependencies": { + "@hpke/common": "^1.10.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@hpke/dhkem-x448": { + "version": "1.8.0", + "integrity": "sha1-6NVrT7sc8KPV5fFAJpBa0yhp618=", + "dev": true, + "license": "MIT", + "dependencies": { + "@hpke/common": "^1.10.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", @@ -2414,6 +2472,22 @@ "node": ">=8" } }, + "node_modules/hpke-js": { + "version": "1.8.0", + "integrity": "sha1-0Sx/4a7uI0/tveC56r8pBPWZhrQ=", + "dev": true, + "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/ieee754": { "version": "1.2.1", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", diff --git a/nodejs/package.json b/nodejs/package.json index 1a0e11a893..5af5d28093 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.92-0", + "copilotCliVersion": "1.0.95-2", "description": "TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC", "main": "./dist/cjs/index.js", "types": "./dist/index.d.ts", @@ -80,6 +80,7 @@ "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", @@ -97,6 +98,7 @@ }, "files": [ "dist/**/*", + "!dist/tsconfig.tsbuildinfo", "docs/**/*", "README.md" ] diff --git a/nodejs/scripts/package-release.ts b/nodejs/scripts/package-release.ts index 90ad30615c..7cd973a833 100644 --- a/nodejs/scripts/package-release.ts +++ b/nodejs/scripts/package-release.ts @@ -22,6 +22,7 @@ import { export const JAVA_CLASSIFIERS = { "linux-arm64": "linux.arm64", "linuxmusl-x64": "linuxmusl.x64", + "linuxmusl-arm64": "linuxmusl.arm64", "win32-x64": "win32", "win32-arm64": "win32.arm64", "darwin-arm64": "darwin", diff --git a/nodejs/scripts/publish-release.ts b/nodejs/scripts/publish-release.ts index 292926d409..4a164d7e92 100644 --- a/nodejs/scripts/publish-release.ts +++ b/nodejs/scripts/publish-release.ts @@ -24,6 +24,13 @@ type NativeCommand = ( env?: NodeJS.ProcessEnv ) => string; +type GithubRelease = { + tag_name: string; + draft: boolean; + prerelease: boolean; + published_at: string | null; +}; + const runNativeCommand: NativeCommand = (command, args, cwd, env = process.env) => execFileSync(command, args, { cwd, @@ -232,12 +239,7 @@ export async function publishJava( } export function previousSdkReleaseTag( - releases: { - tag_name: string; - draft: boolean; - prerelease: boolean; - published_at: string | null; - }[], + releases: GithubRelease[], identity: ReleaseIdentity ): string | undefined { return releases @@ -321,9 +323,24 @@ export async function publishSourceRelease( assert.equal(release.draft, false, "Existing SDK release is a draft"); return; } - const releases = JSON.parse( - gh(["api", "--paginate", "--slurp", `repos/${repository}/releases?per_page=100`]) - ).flat(); + let releases: GithubRelease[]; + if (identity.channel === "latest") { + try { + releases = [JSON.parse(gh(["api", `repos/${repository}/releases/latest`]))]; + } catch (error) { + if (!/\(HTTP 404\)/.test(String((error as { stderr?: string }).stderr))) throw error; + releases = []; + } + } else { + const output = gh([ + "api", + "--paginate", + "--jq", + ".[] | {tag_name, draft, prerelease, published_at}", + `repos/${repository}/releases?per_page=100`, + ]); + releases = output ? output.split("\n").map((line) => JSON.parse(line)) : []; + } const previous = previousSdkReleaseTag(releases, identity); gh([ "release", diff --git a/nodejs/scripts/unified-release.ts b/nodejs/scripts/unified-release.ts index 56b1b2ddae..5e1477ceb5 100644 --- a/nodejs/scripts/unified-release.ts +++ b/nodejs/scripts/unified-release.ts @@ -91,13 +91,12 @@ export function publicationSdks( destination !== "public" || visibility === "public", "Internal releases cannot publish publicly" ); - // Python publishes in the caller so PyPI authentication and attestations share one workflow identity. - return packagedSdks(channel, visibility).filter( - (language) => - language !== "python" && - (destination === "public" || - language === "nodejs" || - (channel !== "unstable" && language === "dotnet")) + // Public Python publishes in the caller so PyPI authentication and attestations share one workflow identity. + return packagedSdks(channel, visibility).filter((language) => + destination === "public" + ? language !== "python" + : language === "nodejs" || + (channel !== "unstable" && (language === "dotnet" || language === "python")) ); } diff --git a/nodejs/src/canvas.ts b/nodejs/src/canvas.ts index aeb1f00ec4..b7c2254c4f 100644 --- a/nodejs/src/canvas.ts +++ b/nodejs/src/canvas.ts @@ -49,7 +49,12 @@ export interface CanvasAction { description?: string; /** Optional JSON Schema for the action's `input` payload. */ inputSchema?: CanvasJsonSchema; - /** Required per-action dispatch handler. */ + /** + * Required per-action dispatch handler. The returned value becomes the + * `invoke_canvas_action` tool result. Return a `ToolResultObject` + * (with `binaryResultsForLlm`) to send text and images to the model, as a + * tool handler would; any other value is rendered to the model as JSON text. + */ handler: (ctx: CanvasProviderInvokeActionRequest) => Promise | unknown; } diff --git a/nodejs/src/cliVersion.ts b/nodejs/src/cliVersion.ts index 4a52758969..833739e796 100644 --- a/nodejs/src/cliVersion.ts +++ b/nodejs/src/cliVersion.ts @@ -1,3 +1,3 @@ -export const COPILOT_CLI_VERSION = "1.0.92-0"; +export const COPILOT_CLI_VERSION = "1.0.95-2"; export const COPILOT_CLI_USE_NPM_PACKAGE = false; diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts index 858697fe8a..fd82d52ada 100644 --- a/nodejs/src/client.ts +++ b/nodejs/src/client.ts @@ -114,6 +114,7 @@ interface ExtensionOrchestrationContributions { */ const MIN_PROTOCOL_VERSION = 3; const RUNTIME_SHUTDOWN_TIMEOUT_MS = 10_000; +const CLOUD_SESSION_CLEANUP_TIMEOUT_MS = 10_000; type DrainingMessageConnection = MessageConnection & { drain: () => Promise }; @@ -899,6 +900,14 @@ export class CopilotClient { "SessionFsConfig declares capabilities.sqlite but the provider does not implement sqlite." ); } + if ( + this.sessionFsConfig.capabilities?.binary && + (!provider.readFileBytes || !provider.writeFileBytes) + ) { + throw new Error( + "SessionFsConfig declares capabilities.binary but the provider does not implement readFileBytes and writeFileBytes." + ); + } session.clientSessionApis.sessionFs = createSessionFsAdapter(provider); } @@ -1653,6 +1662,9 @@ export class CopilotClient { if (config.gitHubToken !== undefined && config.gitHubTokenProvider !== undefined) { throw new Error("gitHubToken and gitHubTokenProvider are mutually exclusive"); } + if (config.cloud != null && config.skillProvider) { + throw new Error("Skill providers are not supported for cloud sessions."); + } if (!this.connection) { await this.start(); } @@ -1736,19 +1748,25 @@ export class CopilotClient { if (config.hooks) { s.registerHooks(config.hooks); } + if (config.skillProvider) { + s.registerSkillProvider(config.skillProvider); + } if (transformCallbacks) { s.registerTransformCallbacks(transformCallbacks); } if (config.onEvent) { s.on(config.onEvent); } - this.sessions.set(sessionId, s); + // Session-FS setup can throw, so finish it before the session + // becomes routable; a failed open must not leave handlers behind. this.setupSessionFs(s, config); + this.sessions.set(sessionId, s); return s; }; let session: CopilotSession | undefined; let registeredId: string | undefined; + let uninitializedCloudSessionId: string | undefined; // Pre-register non-cloud sessions BEFORE issuing the RPC so any // session-scoped requests the CLI emits during `session.create` @@ -1788,6 +1806,7 @@ export class CopilotClient { isTerminal: tool.isTerminal, })), toolSearch: config.toolSearch, + imageGeneration: config.imageGeneration, canvases: config.canvases?.map((canvas) => canvas.declaration), requestCanvasRenderer: config.requestCanvasRenderer, requestExtensions: config.requestExtensions, @@ -1852,6 +1871,7 @@ export class CopilotClient { enableSessionStore: config.enableSessionStore, enableSkills: config.enableSkills, skillDirectories: config.skillDirectories, + ...(config.skillProvider ? { hasSkillProvider: true } : {}), pluginDirectories: config.pluginDirectories, instructionDirectories: config.instructionDirectories, disabledSkills: config.disabledSkills, @@ -1865,6 +1885,7 @@ export class CopilotClient { featureFlags: config.featureFlags, expAssignments: config.expAssignments, enableManagedSettings: config.enableManagedSettings, + enforceManagedModelDefaults: config.enforceManagedModelDefaults, managedSettings: config.managedSettings, }); @@ -1888,7 +1909,9 @@ export class CopilotClient { if (session === undefined) { // Cloud / server-assigned path: register the session now that // the CLI has told us which id it chose. + uninitializedCloudSessionId = returnedSessionId; session = initializeSession(returnedSessionId); + uninitializedCloudSessionId = undefined; registeredId = returnedSessionId; } this.assignGitHubTokenProvider(gitHubTokenProviderRegistrationId, returnedSessionId); @@ -1911,6 +1934,20 @@ export class CopilotClient { if (gitHubTokenProviderRegistrationId !== undefined) { this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId); } + if (uninitializedCloudSessionId !== undefined) { + try { + await withTimeout( + this.deleteSession(uninitializedCloudSessionId), + CLOUD_SESSION_CLEANUP_TIMEOUT_MS, + `session.delete timed out after ${CLOUD_SESSION_CLEANUP_TIMEOUT_MS}ms` + ); + } catch (cleanupError) { + throw new AggregateError( + [e, cleanupError], + "Failed to initialize and delete cloud session" + ); + } + } throw e; } @@ -2015,6 +2052,9 @@ export class CopilotClient { if (config.hooks) { session.registerHooks(config.hooks); } + if (config.skillProvider) { + session.registerSkillProvider(config.skillProvider); + } const modeDefaults = this.configDefaultsForMode(); config = { ...modeDefaults, ...config }; @@ -2031,10 +2071,13 @@ export class CopilotClient { if (config.onEvent) { session.on(config.onEvent); } - this.sessions.set(sessionId, session); + // Validation and session-FS setup can throw, so finish them before the + // replacement session becomes routable over any resident one. + const toolFilterOptions = this.resolveToolFilterOptions(config); this.setupSessionFs(session, config); + const replacedSession = this.sessions.get(sessionId); + this.sessions.set(sessionId, session); - const toolFilterOptions = this.resolveToolFilterOptions(config); const gitHubTokenProviderRegistrationId = this.registerGitHubTokenProvider( config.gitHubTokenProvider, sessionId @@ -2077,6 +2120,7 @@ export class CopilotClient { isTerminal: tool.isTerminal, })), toolSearch: config.toolSearch, + imageGeneration: config.imageGeneration, canvases: config.canvases?.map((canvas) => canvas.declaration), workflows: workflows?.map((workflow) => workflow.meta), requestCanvasRenderer: config.requestCanvasRenderer, @@ -2133,6 +2177,7 @@ export class CopilotClient { defaultAgent: config.defaultAgent, agent: config.agent, skillDirectories: config.skillDirectories, + ...(config.skillProvider ? { hasSkillProvider: true } : {}), pluginDirectories: config.pluginDirectories, instructionDirectories: config.instructionDirectories, disabledSkills: config.disabledSkills, @@ -2148,6 +2193,7 @@ export class CopilotClient { featureFlags: config.featureFlags, expAssignments: config.expAssignments, enableManagedSettings: config.enableManagedSettings, + enforceManagedModelDefaults: config.enforceManagedModelDefaults, managedSettings: config.managedSettings, ...(extensionOptions?.requestedEnvironmentVariables ? { @@ -2196,7 +2242,15 @@ export class CopilotClient { this.commitGitHubTokenProvider(sessionId, gitHubTokenProviderRegistrationId); } catch (e) { session._markDisconnected(); - this.sessions.delete(sessionId); + // The runtime keeps the resident session's bindings when a resume + // fails, so its callbacks must keep reaching the replaced session. + if (this.sessions.get(sessionId) === session) { + if (replacedSession) { + this.sessions.set(sessionId, replacedSession); + } else { + this.sessions.delete(sessionId); + } + } if (gitHubTokenProviderRegistrationId !== undefined) { this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId); } @@ -2217,7 +2271,7 @@ export class CopilotClient { * This connection owns the host. Disposing it or disconnecting the client * stops the listener without deleting sessions. The host uses a separate * SDK connection to the same runtime, not another runtime process. - * Only one AHP host may own the catalog in an effective Copilot home + * Only one AHP host may own a compute-scoped catalog in an effective Copilot home * at a time. Other SDK clients and sessions remain usable in that home. * Select localServer, githubEnvironment, or both. An empty localServer * selects 127.0.0.1 on an available port. `onExit` runs at most once; owner disconnection cannot @@ -2235,6 +2289,7 @@ export class CopilotClient { const rpc = this.rpc; const hostId = randomUUID(); const { + computeId, localServer, githubEnvironment, onExit, @@ -2255,6 +2310,7 @@ export class CopilotClient { try { const info = await rpc.host.start({ hostId, + computeId, localServer, githubEnvironment, sessionFactory: createSession ? true : undefined, @@ -2265,7 +2321,8 @@ export class CopilotClient { async () => { await rpc.host.dispose({ hostId }); }, - (sessionId) => rpc.host.publishSession({ hostId, sessionId }) + (sessionId) => rpc.host.publishSession({ hostId, sessionId }), + () => rpc.host.listSessions({ hostId }) ); } catch (error) { this.hostExitCallbacks.delete(hostId); @@ -3487,6 +3544,23 @@ export class CopilotClient { } ); + // Internal experimental callbacks backing `SessionConfigBase.skillProvider`. + this.connection.onRequest( + "skillProvider.list", + async (params: { sessionId: string }, token: CancellationToken) => + await this.resolveSkillProviderSession(params)._handleSkillProviderList(token) + ); + this.connection.onRequest( + "skillProvider.read", + async (params: { sessionId: string; name: string }, token: CancellationToken) => { + const session = this.resolveSkillProviderSession(params); + if (typeof params.name !== "string") { + throw new Error("Invalid skillProvider.read payload"); + } + return await session._handleSkillProviderRead(params.name, token); + } + ); + const connection = this.connection; const messageWriter = this.messageWriter; const cliProcess = this.isExternalServer ? null : this.cliProcess; @@ -3710,6 +3784,17 @@ export class CopilotClient { return { response }; } + private resolveSkillProviderSession(params: { sessionId: string }): CopilotSession { + if (!params || typeof params.sessionId !== "string") { + throw new Error("Invalid skillProvider payload"); + } + const session = this.sessions.get(params.sessionId); + if (!session) { + throw new Error(`Session not found: ${params.sessionId}`); + } + return session; + } + private async handleHooksInvoke(params: { sessionId: string; hookType: string; diff --git a/nodejs/src/extension.ts b/nodejs/src/extension.ts index e7bb21e3b2..f6adc32745 100644 --- a/nodejs/src/extension.ts +++ b/nodejs/src/extension.ts @@ -24,7 +24,7 @@ export { export type JoinSessionConfig = Omit< ResumeSessionConfig, - "onPermissionRequest" | "extensionSdkPath" + "onPermissionRequest" | "extensionSdkPath" | "skillProvider" | "imageGeneration" > & { onPermissionRequest?: PermissionHandler; /** @@ -124,16 +124,25 @@ export async function joinSession(config: JoinSessionConfig = {}): Promise