diff --git a/.github/actions/java-test-report/action.yml b/.github/actions/java-test-report/action.yml
index eedf053725..4ea110cb13 100644
--- a/.github/actions/java-test-report/action.yml
+++ b/.github/actions/java-test-report/action.yml
@@ -126,7 +126,8 @@ runs:
if [ -n "$covered" ] && [ -n "$missed" ]; then
local total=$((covered + missed))
if [ "$total" -gt 0 ]; then
- echo "scale=1; $covered * 100 / $total" | bc
+ awk -v covered="$covered" -v total="$total" \
+ 'BEGIN { printf "%.1f\n", covered * 100 / total }'
else
echo "0"
fi
diff --git a/.github/workflows/java-publish-maven.yml b/.github/workflows/java-publish-maven.yml
index 1744a697e1..fd07aae155 100644
--- a/.github/workflows/java-publish-maven.yml
+++ b/.github/workflows/java-publish-maven.yml
@@ -1,9 +1,6 @@
name: "Java Publish to Maven Central"
env:
- # Disable Husky Git hooks in CI to prevent local development hooks
- # (e.g., pre-commit formatting checks) from running during automated
- # workflows that perform git commits and pushes.
HUSKY: 0
on:
@@ -40,7 +37,7 @@ on:
outputs:
mavenPublished:
description: "Whether the Java package was published to Maven Central"
- value: ${{ jobs.publish-maven.outputs.published }}
+ value: ${{ jobs.deploy-maven.outputs.published }}
secrets:
JAVA_RELEASE_TOKEN:
required: true
@@ -56,8 +53,7 @@ on:
required: true
permissions:
- contents: write
- id-token: write
+ contents: read
concurrency:
group: publish-maven
@@ -70,11 +66,6 @@ jobs:
steps:
- name: Verify JAVA_RELEASE_TOKEN can push to repository
run: |
- # JAVA_RELEASE_TOKEN is used by actions/checkout and for:
- # - git push origin main (doc updates)
- # - mvn release:prepare -DpushChanges=true (release commits + tags)
- # - git revert + push (rollback on failure)
- # It must have push (contents:write) permission on this repo.
PUSH=$(gh api repos/${{ github.repository }} --jq '.permissions.push // false')
if [ "$PUSH" != "true" ]; then
echo "::error::JAVA_RELEASE_TOKEN lacks push permission on ${{ github.repository }}. It is required for pushing release commits and tags to main."
@@ -84,17 +75,24 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.JAVA_RELEASE_TOKEN }}
- publish-maven:
- name: Publish Java SDK to Maven Central
+ prepare-release:
+ name: Prepare Java release
needs: preflight
runs-on: ubuntu-latest
+ permissions:
+ contents: write
defaults:
run:
shell: bash
working-directory: ./java
outputs:
- version: ${{ steps.versions.outputs.release_version }}
- published: ${{ steps.publish-maven.outcome == 'success' }}
+ release_version: ${{ steps.versions.outputs.release_version }}
+ development_version: ${{ steps.versions.outputs.development_version }}
+ release_tag: ${{ steps.release-identity.outputs.release_tag }}
+ tag_commit: ${{ steps.release-identity.outputs.tag_commit }}
+ pre_prepare_commit: ${{ steps.pre-prepare.outputs.pre_prepare_commit }}
+ post_prepare_commit: ${{ steps.release-identity.outputs.post_prepare_commit }}
+ docs_commit: ${{ steps.update-docs.outputs.docs_commit_sha }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
@@ -108,88 +106,66 @@ jobs:
- uses: ./.github/actions/setup-copilot
- - name: Set up JDK 25
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
with:
java-version: "25"
distribution: "microsoft"
cache: "maven"
- server-id: central
- server-username: MAVEN_USERNAME
- server-password: MAVEN_PASSWORD
- gpg-private-key: ${{ secrets.JAVA_GPG_SECRET_KEY }}
- gpg-passphrase: JAVA_GPG_PASSPHRASE
- name: Determine versions
id: versions
- working-directory: ./java
run: |
CURRENT_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
echo "Current pom.xml version: $CURRENT_VERSION"
- # Determine release version
if [ -n "${{ inputs.releaseVersion }}" ]; then
RELEASE_VERSION="${{ inputs.releaseVersion }}"
else
- # Remove -SNAPSHOT suffix if present
RELEASE_VERSION="${CURRENT_VERSION%-SNAPSHOT}"
fi
echo "Release version: $RELEASE_VERSION"
- # Determine next development version
if [ -n "${{ inputs.developmentVersion }}" ]; then
DEV_VERSION="${{ inputs.developmentVersion }}"
if [[ "$DEV_VERSION" != *-SNAPSHOT ]]; then
- echo "::error::developmentVersion '${DEV_VERSION}' must end with '-SNAPSHOT' (e.g., '${DEV_VERSION}-SNAPSHOT'). The maven-release-plugin requires the next development version to be a snapshot."
+ echo "::error::developmentVersion '${DEV_VERSION}' must end with '-SNAPSHOT'."
exit 1
fi
else
- # Split version: supports "0.1.32", "0.1.32-preview.0", "0.1.32-java.0", and "0.1.32-java-preview.0" formats
- # Validate RELEASE_VERSION format explicitly to provide clear errors
if ! echo "$RELEASE_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-(preview|(beta-)?java(-preview)?)\.[0-9]+)?$'; then
- echo "Error: RELEASE_VERSION '$RELEASE_VERSION' is invalid. Expected format: M.M.P, M.M.P-preview.N, M.M.P-java.N, M.M.P-java-preview.N, M.M.P-beta-java.N, or M.M.P-beta-java-preview.N (e.g., 1.2.3, 1.2.3-preview.0, 1.2.3-java.0, 1.2.3-java-preview.0, 1.2.3-beta-java.0, or 1.2.3-beta-java-preview.0)." >&2
+ echo "Error: RELEASE_VERSION '$RELEASE_VERSION' is invalid." >&2
exit 1
fi
- # Extract the base M.M.P portion (before any qualifier)
BASE_VERSION=$(echo "$RELEASE_VERSION" | grep -oE '^[0-9]+\.[0-9]+\.[0-9]+')
QUALIFIER=$(echo "$RELEASE_VERSION" | sed "s|^${BASE_VERSION}||")
IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION"
- NEXT_PATCH=$((PATCH + 1))
- DEV_VERSION="${MAJOR}.${MINOR}.${NEXT_PATCH}${QUALIFIER}-SNAPSHOT"
+ DEV_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))${QUALIFIER}-SNAPSHOT"
fi
- echo "Next development version: $DEV_VERSION"
-
- echo "release_version=$RELEASE_VERSION" >> $GITHUB_OUTPUT
- echo "dev_version=$DEV_VERSION" >> $GITHUB_OUTPUT
- echo "### Version Summary" >> $GITHUB_STEP_SUMMARY
- echo "- **Release version:** $RELEASE_VERSION" >> $GITHUB_STEP_SUMMARY
- echo "- **Next development version:** $DEV_VERSION" >> $GITHUB_STEP_SUMMARY
+ echo "release_version=$RELEASE_VERSION" >> "$GITHUB_OUTPUT"
+ echo "development_version=$DEV_VERSION" >> "$GITHUB_OUTPUT"
- name: Update documentation with release version
id: update-docs
- working-directory: ./java
run: |
VERSION="${{ steps.versions.outputs.release_version }}"
- DEV_VERSION="${{ steps.versions.outputs.dev_version }}"
+ DEV_VERSION="${{ steps.versions.outputs.development_version }}"
./scripts/test-update-documentation-versions.sh
./scripts/update-documentation-versions.sh "$VERSION" "$DEV_VERSION" README.md sdk/jbang-example.java
-
- # Commit the documentation changes before release:prepare (requires clean working directory)
git add README.md sdk/jbang-example.java
git commit -m "docs: update version references to ${VERSION}"
-
- # Save the commit SHA for potential rollback
- echo "docs_commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
-
+ echo "docs_commit_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
git push origin main
+ - name: Record rollback base
+ id: pre-prepare
+ run: echo "pre_prepare_commit=$(git rev-parse HEAD^)" >> "$GITHUB_OUTPUT"
+
- name: Prepare Release
- working-directory: ./java
run: |
mvn -B release:prepare \
-DreleaseVersion=${{ steps.versions.outputs.release_version }} \
- -DdevelopmentVersion=${{ steps.versions.outputs.dev_version }} \
+ -DdevelopmentVersion=${{ steps.versions.outputs.development_version }} \
-DtagNameFormat=java/v@{project.version} \
-DpushChanges=true \
-Darguments="-DskipTests"
@@ -198,38 +174,594 @@ jobs:
MAVEN_PASSWORD: ${{ secrets.JAVA_MAVEN_CENTRAL_PASSWORD }}
JAVA_GPG_PASSPHRASE: ${{ secrets.JAVA_GPG_PASSPHRASE }}
- - name: Perform Release and Deploy to Maven Central
- id: publish-maven
+ - name: Record immutable release identity
+ id: release-identity
+ run: |
+ TAG="java/v${{ steps.versions.outputs.release_version }}"
+ TAG_COMMIT=$(git rev-parse "${TAG}^{commit}")
+ POST_PREPARE_COMMIT=$(git rev-parse HEAD)
+ echo "release_tag=$TAG" >> "$GITHUB_OUTPUT"
+ echo "tag_commit=$TAG_COMMIT" >> "$GITHUB_OUTPUT"
+ echo "post_prepare_commit=$POST_PREPARE_COMMIT" >> "$GITHUB_OUTPUT"
+
+ build-linux-arm64-classifier:
+ name: Build Linux ARM64 native classifier
+ needs: prepare-release
+ runs-on: ubuntu-24.04-arm
+ permissions:
+ contents: read
+ defaults:
+ run:
+ shell: bash
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ needs.prepare-release.outputs.release_tag }}
+ fetch-depth: 1
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate linux-arm64 classifier
+ run: |
+ set -euo pipefail
+ SOURCE_COMMIT=$(git rev-parse HEAD)
+ if [ "$SOURCE_COMMIT" != "${{ needs.prepare-release.outputs.tag_commit }}" ]; then
+ echo "::error::Checked out $SOURCE_COMMIT instead of the prepared tag commit."
+ exit 1
+ fi
+ node copilot-native/scripts/validate-native-host.mjs linux-arm64
+ mvn -B -pl copilot-native package -DskipTests -Dcopilot.native.libc=glibc
+ VERSION="${{ needs.prepare-release.outputs.release_version }}"
+ JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-linux-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 linux-arm64 "$JAR" "$(basename "$JAR")" ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder "$PRIMARY_JAR"
+ MANIFEST="copilot-native/target/linux-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")"
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-linux-arm64-release-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ needs.prepare-release.outputs.release_version }}-linux-arm64.jar
+ java/copilot-native/target/linux-arm64-${{ needs.prepare-release.outputs.release_version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ build-windows-classifier:
+ name: Build Windows native classifier
+ needs: prepare-release
+ runs-on: windows-latest
+ permissions:
+ contents: read
+ defaults:
+ run:
+ shell: pwsh
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ needs.prepare-release.outputs.release_tag }}
+ fetch-depth: 1
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate win32-x64 classifier
+ run: |
+ $sourceCommit = git rev-parse HEAD
+ if ($sourceCommit -ne '${{ needs.prepare-release.outputs.tag_commit }}') {
+ throw "Checked out $sourceCommit instead of the prepared tag commit."
+ }
+ node copilot-native/scripts/validate-native-host.mjs win32-x64
+ mvn -B -pl copilot-native package -DskipTests
+ $version = '${{ needs.prepare-release.outputs.release_version }}'
+ $jar = "copilot-native/target/copilot-sdk-java-runtime-$version-win32-x64.jar"
+ $primaryJar = "copilot-native/target/copilot-sdk-java-runtime-$version.jar"
+ if (-not (Test-Path -LiteralPath $jar -PathType Leaf)) {
+ throw "Expected Windows classifier was not produced: $jar"
+ }
+ node copilot-native/scripts/validate-native-artifact.mjs classifier win32-x64 $jar ([IO.Path]::GetFileName($jar)) ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder $primaryJar
+ $manifest = "copilot-native/target/win32-x64-$version.sha256"
+ $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $jar).Hash.ToLowerInvariant()
+ "$hash $([IO.Path]::GetFileName($jar))" | Set-Content -NoNewline -Encoding ascii $manifest
+ node copilot-native/scripts/validate-native-artifact.mjs checksum $jar $manifest ([IO.Path]::GetFileName($jar))
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-win32-x64-release-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ needs.prepare-release.outputs.release_version }}-win32-x64.jar
+ java/copilot-native/target/win32-x64-${{ needs.prepare-release.outputs.release_version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ build-windows-arm64-classifier:
+ name: Build Windows ARM64 native classifier
+ needs: prepare-release
+ runs-on: windows-11-arm
+ permissions:
+ contents: read
+ defaults:
+ run:
+ shell: pwsh
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ needs.prepare-release.outputs.release_tag }}
+ fetch-depth: 1
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate win32-arm64 classifier
+ run: |
+ $sourceCommit = git rev-parse HEAD
+ if ($sourceCommit -ne '${{ needs.prepare-release.outputs.tag_commit }}') {
+ throw "Checked out $sourceCommit instead of the prepared tag commit."
+ }
+ node copilot-native/scripts/validate-native-host.mjs win32-arm64
+ mvn -B -pl copilot-native package -DskipTests
+ $version = '${{ needs.prepare-release.outputs.release_version }}'
+ $jar = "copilot-native/target/copilot-sdk-java-runtime-$version-win32-arm64.jar"
+ $primaryJar = "copilot-native/target/copilot-sdk-java-runtime-$version.jar"
+ if (-not (Test-Path -LiteralPath $jar -PathType Leaf)) {
+ throw "Expected Windows ARM64 classifier was not produced: $jar"
+ }
+ node copilot-native/scripts/validate-native-artifact.mjs classifier win32-arm64 $jar ([IO.Path]::GetFileName($jar)) ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder $primaryJar
+ $manifest = "copilot-native/target/win32-arm64-$version.sha256"
+ $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $jar).Hash.ToLowerInvariant()
+ "$hash $([IO.Path]::GetFileName($jar))" | Set-Content -NoNewline -Encoding ascii $manifest
+ node copilot-native/scripts/validate-native-artifact.mjs checksum $jar $manifest ([IO.Path]::GetFileName($jar))
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-win32-arm64-release-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ needs.prepare-release.outputs.release_version }}-win32-arm64.jar
+ java/copilot-native/target/win32-arm64-${{ needs.prepare-release.outputs.release_version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ build-darwin-classifier:
+ name: Build Darwin native classifier
+ needs: prepare-release
+ runs-on: macos-26
+ permissions:
+ contents: read
+ defaults:
+ run:
+ shell: bash
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ needs.prepare-release.outputs.release_tag }}
+ fetch-depth: 1
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate darwin-arm64 classifier
+ run: |
+ set -euo pipefail
+ SOURCE_COMMIT=$(git rev-parse HEAD)
+ if [ "$SOURCE_COMMIT" != "${{ needs.prepare-release.outputs.tag_commit }}" ]; then
+ echo "::error::Checked out $SOURCE_COMMIT instead of the prepared tag commit."
+ exit 1
+ fi
+ node copilot-native/scripts/validate-native-host.mjs darwin-arm64
+ mvn -B -pl copilot-native package -DskipTests
+ VERSION="${{ needs.prepare-release.outputs.release_version }}"
+ JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-darwin-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 darwin-arm64 "$JAR" "$(basename "$JAR")" ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder "$PRIMARY_JAR"
+ MANIFEST="copilot-native/target/darwin-arm64-$VERSION.sha256"
+ HASH=$(shasum -a 256 "$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")"
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-darwin-arm64-release-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ needs.prepare-release.outputs.release_version }}-darwin-arm64.jar
+ java/copilot-native/target/darwin-arm64-${{ needs.prepare-release.outputs.release_version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ deploy-maven:
+ name: Deploy Java release to Maven Central
+ needs:
+ [
+ prepare-release,
+ build-linux-arm64-classifier,
+ build-windows-classifier,
+ build-windows-arm64-classifier,
+ build-darwin-classifier,
+ ]
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ defaults:
+ run:
+ shell: bash
working-directory: ./java
+ outputs:
+ version: ${{ needs.prepare-release.outputs.release_version }}
+ published: ${{ steps.publish-maven.outcome == 'success' }}
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ needs.prepare-release.outputs.release_tag }}
+ fetch-depth: 1
+ persist-credentials: false
+
+ - uses: ./.github/actions/setup-copilot
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+ server-id: central
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ gpg-private-key: ${{ secrets.JAVA_GPG_SECRET_KEY }}
+ gpg-passphrase: JAVA_GPG_PASSPHRASE
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-linux-arm64-release-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/java-native-linux-arm64
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-win32-x64-release-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/java-native-win32-x64
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-win32-arm64-release-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/java-native-win32-arm64
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-darwin-arm64-release-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/java-native-darwin-arm64
+
+ - name: Verify immutable source and Linux ARM64 classifier
+ id: linux-arm64-artifact
+ run: |
+ SOURCE_COMMIT=$(git rev-parse HEAD)
+ if [ "$SOURCE_COMMIT" != "${{ needs.prepare-release.outputs.tag_commit }}" ]; then
+ echo "::error::Checked out $SOURCE_COMMIT instead of the prepared tag commit."
+ exit 1
+ fi
+ VERSION="${{ needs.prepare-release.outputs.release_version }}"
+ ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-linux-arm64"
+ JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-linux-arm64.jar"
+ MANIFEST="$ARTIFACT_DIRECTORY/linux-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 linux-arm64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE"
+ echo "linux_arm64_jar=$JAR" >> "$GITHUB_OUTPUT"
+ echo "linux_arm64_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT"
+
+ - name: Verify immutable source and Windows classifier
+ id: windows-artifact
run: |
- mvn -B release:perform \
- -Dgoals="deploy" \
- -Darguments="-DskipTests -Prelease"
+ SOURCE_COMMIT=$(git rev-parse HEAD)
+ if [ "$SOURCE_COMMIT" != "${{ needs.prepare-release.outputs.tag_commit }}" ]; then
+ echo "::error::Checked out $SOURCE_COMMIT instead of the prepared tag commit."
+ exit 1
+ fi
+ VERSION="${{ needs.prepare-release.outputs.release_version }}"
+ ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-win32-x64"
+ JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-win32-x64.jar"
+ MANIFEST="$ARTIFACT_DIRECTORY/win32-x64-$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 win32-x64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE"
+ echo "windows_jar=$JAR" >> "$GITHUB_OUTPUT"
+ echo "windows_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT"
+
+ - name: Verify immutable source and Darwin classifier
+ id: darwin-artifact
+ run: |
+ SOURCE_COMMIT=$(git rev-parse HEAD)
+ if [ "$SOURCE_COMMIT" != "${{ needs.prepare-release.outputs.tag_commit }}" ]; then
+ echo "::error::Checked out $SOURCE_COMMIT instead of the prepared tag commit."
+ exit 1
+ fi
+ VERSION="${{ needs.prepare-release.outputs.release_version }}"
+ ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-darwin-arm64"
+ JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-darwin-arm64.jar"
+ MANIFEST="$ARTIFACT_DIRECTORY/darwin-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 darwin-arm64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE"
+ echo "darwin_jar=$JAR" >> "$GITHUB_OUTPUT"
+ echo "darwin_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT"
+
+ - name: Verify immutable source and Windows ARM64 classifier
+ id: windows-arm64-artifact
+ run: |
+ SOURCE_COMMIT=$(git rev-parse HEAD)
+ if [ "$SOURCE_COMMIT" != "${{ needs.prepare-release.outputs.tag_commit }}" ]; then
+ echo "::error::Checked out $SOURCE_COMMIT instead of the prepared tag commit."
+ exit 1
+ fi
+ VERSION="${{ needs.prepare-release.outputs.release_version }}"
+ ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-win32-arm64"
+ JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-win32-arm64.jar"
+ MANIFEST="$ARTIFACT_DIRECTORY/win32-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 win32-arm64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE"
+ echo "windows_arm64_jar=$JAR" >> "$GITHUB_OUTPUT"
+ echo "windows_arm64_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT"
+
+ - name: Build Linux classifier and deploy complete release
+ id: publish-maven
+ run: |
+ VERSION="${{ needs.prepare-release.outputs.release_version }}"
+ mvn -B deploy -DskipTests -Prelease -Dcopilot.native.libc=glibc \
+ "-Dcopilot.native.external.linux.arm64.classifier.path=${{ steps.linux-arm64-artifact.outputs.linux_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 }}"
+ LINUX_JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-linux-x64.jar"
+ test -f "$LINUX_JAR"
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ classifier linux-x64 "$LINUX_JAR" "$(basename "$LINUX_JAR")" ..
+ LINUX_SHA=$(sha256sum "$LINUX_JAR" | cut -d ' ' -f 1)
+ GROUP_ID=$(mvn -q -pl copilot-native help:evaluate -Dexpression=project.groupId -DforceStdout)
+ ARTIFACT_ID=$(mvn -q -pl copilot-native help:evaluate -Dexpression=project.artifactId -DforceStdout)
+ POM_VERSION=$(mvn -q -pl copilot-native help:evaluate -Dexpression=project.version -DforceStdout)
+ if [ -z "$GROUP_ID" ] || [ -z "$ARTIFACT_ID" ] || [ "$POM_VERSION" != "$VERSION" ]; then
+ echo "::error::Unexpected copilot-native Maven coordinates: $GROUP_ID:$ARTIFACT_ID:$POM_VERSION (expected version $VERSION)"
+ exit 1
+ fi
+ {
+ echo "### Maven Central Release"
+ echo "- **Version:** $VERSION"
+ echo "- **Source tag:** \`${{ needs.prepare-release.outputs.release_tag }}\`"
+ echo "- **Source commit:** \`${{ needs.prepare-release.outputs.tag_commit }}\`"
+ echo "- **Next development version:** ${{ needs.prepare-release.outputs.development_version }}"
+ echo "- **Repository:** Maven Central"
+ echo ""
+ echo "#### Maven Coordinates"
+ echo ""
+ echo '```xml'
+ echo ""
+ echo " $GROUP_ID"
+ echo " $ARTIFACT_ID"
+ echo " $POM_VERSION"
+ echo ""
+ echo '```'
+ echo ""
+ echo "#### Published Native Classifiers"
+ echo ""
+ echo "| Classifier | Build runner | Artifact | SHA-256 | Status |"
+ echo "| --- | --- | --- | --- | --- |"
+ 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 "| \`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-arm64\` | \`macos-26\` | \`$(basename "${{ steps.darwin-artifact.outputs.darwin_jar }}")\` | \`${{ steps.darwin-artifact.outputs.darwin_sha }}\` | Published |"
+ } >> "$GITHUB_STEP_SUMMARY"
env:
MAVEN_USERNAME: ${{ secrets.JAVA_MAVEN_CENTRAL_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.JAVA_MAVEN_CENTRAL_PASSWORD }}
JAVA_GPG_PASSPHRASE: ${{ secrets.JAVA_GPG_PASSPHRASE }}
- - name: Rollback documentation commit on failure
- if: failure() && steps.update-docs.outputs.docs_commit_sha != ''
+ rollback-release:
+ name: Roll back failed Java release preparation
+ needs:
+ [
+ prepare-release,
+ build-linux-arm64-classifier,
+ build-windows-classifier,
+ build-windows-arm64-classifier,
+ build-darwin-classifier,
+ deploy-maven,
+ ]
+ if: ${{ failure() && needs.prepare-release.outputs.docs_commit != '' }}
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ defaults:
+ run:
+ shell: bash
working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ fetch-depth: 0
+ token: ${{ secrets.JAVA_RELEASE_TOKEN }}
+
+ - name: Safely revert release commits and tag
+ env:
+ DOCS_COMMIT: ${{ needs.prepare-release.outputs.docs_commit }}
+ PREPARE_RESULT: ${{ needs.prepare-release.result }}
+ PRE_PREPARE_COMMIT: ${{ needs.prepare-release.outputs.pre_prepare_commit }}
+ POST_PREPARE_COMMIT: ${{ needs.prepare-release.outputs.post_prepare_commit }}
+ RELEASE_TAG: java/v${{ needs.prepare-release.outputs.release_version }}
+ TAG_COMMIT: ${{ needs.prepare-release.outputs.tag_commit }}
run: |
- echo "Release failed, rolling back documentation commit..."
- git revert --no-edit ${{ steps.update-docs.outputs.docs_commit_sha }}
- git push origin main
+ set -euo pipefail
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git fetch origin main --tags
+ MAIN_COMMIT=$(git rev-parse origin/main)
+ echo "Rollback inspection: main=$MAIN_COMMIT docs=$DOCS_COMMIT pre=$PRE_PREPARE_COMMIT post=$POST_PREPARE_COMMIT tag=$RELEASE_TAG"
+
+ unsafe_rollback() {
+ echo "::error::Unsafe rollback state: $*" >&2
+ exit 1
+ }
+
+ if ! git cat-file -e "${PRE_PREPARE_COMMIT}^{commit}"; then
+ unsafe_rollback "Recorded pre-prepare commit does not exist: $PRE_PREPARE_COMMIT"
+ fi
+ if ! git cat-file -e "${DOCS_COMMIT}^{commit}"; then
+ unsafe_rollback "Recorded documentation commit does not exist: $DOCS_COMMIT"
+ fi
+ if ! git merge-base --is-ancestor "$PRE_PREPARE_COMMIT" "$MAIN_COMMIT"; then
+ unsafe_rollback "Recorded pre-prepare commit is not an ancestor of main."
+ fi
+ if [ "$(git rev-parse "${DOCS_COMMIT}^")" != "$PRE_PREPARE_COMMIT" ]; then
+ unsafe_rollback "Documentation commit is not immediately based on the recorded pre-prepare commit."
+ fi
- # Also run Maven release:rollback to clean up any partial release state
- mvn -B release:rollback || true
+ mapfile -t ROLLBACK_COMMITS < <(git rev-list --reverse "$PRE_PREPARE_COMMIT..$MAIN_COMMIT")
+ FIRST_PARENT_COUNT=$(git rev-list --count --first-parent "$PRE_PREPARE_COMMIT..$MAIN_COMMIT")
+ if [ "${#ROLLBACK_COMMITS[@]}" -ne "$FIRST_PARENT_COUNT" ]; then
+ unsafe_rollback "Release range contains merged history."
+ fi
+ if [ "${#ROLLBACK_COMMITS[@]}" -lt 1 ] || [ "${#ROLLBACK_COMMITS[@]}" -gt 3 ]; then
+ unsafe_rollback "Expected one to three release-preparation commits after the recorded base; found ${#ROLLBACK_COMMITS[@]}."
+ fi
+ if [ "${ROLLBACK_COMMITS[0]}" != "$DOCS_COMMIT" ]; then
+ unsafe_rollback "The first commit after the recorded base is not the recorded documentation commit."
+ fi
+
+ EXPECTED_PARENT="$PRE_PREPARE_COMMIT"
+ for COMMIT in "${ROLLBACK_COMMITS[@]}"; do
+ if git rev-parse -q --verify "${COMMIT}^2" >/dev/null; then
+ unsafe_rollback "Release range contains merge commit $COMMIT."
+ fi
+ if [ "$(git rev-parse "${COMMIT}^")" != "$EXPECTED_PARENT" ]; then
+ unsafe_rollback "Release range is not a linear continuation of the recorded base."
+ fi
+ EXPECTED_PARENT="$COMMIT"
+ done
+
+ RELEASE_VERSION="${RELEASE_TAG#java/v}"
+ if [ "$(git log -1 --format=%s "$DOCS_COMMIT")" != "docs: update version references to $RELEASE_VERSION" ]; then
+ unsafe_rollback "Recorded documentation commit has an unexpected subject."
+ fi
+
+ RELEASE_PREPARE_COMMIT=""
+ if [ "${#ROLLBACK_COMMITS[@]}" -ge 2 ]; then
+ RELEASE_PREPARE_COMMIT="${ROLLBACK_COMMITS[1]}"
+ if [ "$(git log -1 --format=%s "$RELEASE_PREPARE_COMMIT")" != "[maven-release-plugin] prepare release $RELEASE_TAG" ]; then
+ unsafe_rollback "Release-version commit has an unexpected subject."
+ fi
+ fi
+ if [ "${#ROLLBACK_COMMITS[@]}" -eq 3 ] && [ "$(git log -1 --format=%s "${ROLLBACK_COMMITS[2]}")" != "[maven-release-plugin] prepare for next development iteration" ]; then
+ unsafe_rollback "Development-version commit has an unexpected subject."
+ fi
+
+ TAG_OBJECT=$(git ls-remote --refs origin "refs/tags/$RELEASE_TAG" | awk '{print $1}')
+ if [ -n "$TAG_OBJECT" ]; then
+ git fetch --no-tags origin "+refs/tags/$RELEASE_TAG:refs/tags/$RELEASE_TAG"
+ REMOTE_TAG_COMMIT=$(git rev-parse "${RELEASE_TAG}^{commit}")
+ if [ -z "$RELEASE_PREPARE_COMMIT" ] || [ "$REMOTE_TAG_COMMIT" != "$RELEASE_PREPARE_COMMIT" ]; then
+ unsafe_rollback "Release tag does not point to the guarded release-version commit."
+ fi
+ elif [ -n "$TAG_COMMIT" ]; then
+ unsafe_rollback "Recorded release tag is absent from the remote."
+ fi
+
+ if [ "$PREPARE_RESULT" = "success" ]; then
+ if [ -z "$POST_PREPARE_COMMIT" ] || [ -z "$TAG_COMMIT" ]; then
+ unsafe_rollback "Successful preparation did not record its immutable release identity."
+ fi
+ if [ "$MAIN_COMMIT" != "$POST_PREPARE_COMMIT" ]; then
+ unsafe_rollback "main has advanced beyond the recorded post-prepare commit."
+ fi
+ if [ -z "$TAG_OBJECT" ] || [ "$REMOTE_TAG_COMMIT" != "$TAG_COMMIT" ]; then
+ unsafe_rollback "Remote release tag does not match the recorded tag commit."
+ fi
+ fi
+
+ git checkout -B release-rollback "$MAIN_COMMIT"
+ git revert --no-edit "$PRE_PREPARE_COMMIT..$MAIN_COMMIT"
+ if [ -n "$TAG_OBJECT" ]; then
+ git push --atomic \
+ "--force-with-lease=refs/heads/main:$MAIN_COMMIT" \
+ "--force-with-lease=refs/tags/$RELEASE_TAG:$TAG_OBJECT" \
+ origin HEAD:refs/heads/main ":refs/tags/$RELEASE_TAG"
+ else
+ git push \
+ "--force-with-lease=refs/heads/main:$MAIN_COMMIT" \
+ origin HEAD:refs/heads/main
+ fi
+ echo "Release preparation rollback completed after guarded history inspection."
deploy-site:
name: Deploy Documentation Site
- needs: [preflight, publish-maven]
- if: github.ref == 'refs/heads/main'
+ needs: [preflight, deploy-maven]
+ if: github.ref == 'refs/heads/main' && needs.deploy-maven.outputs.published == 'true'
runs-on: ubuntu-latest
steps:
- name: Trigger site deployment on standalone repo
run: |
- VERSION="${{ needs.publish-maven.outputs.version }}"
+ VERSION="${{ needs.deploy-maven.outputs.version }}"
TAG="java/v${VERSION}"
PUBLISH_AS_LATEST=true
if [ "${{ inputs.prerelease }}" = "true" ]; then
@@ -241,7 +773,9 @@ jobs:
-f version="${VERSION}" \
-f publish_as_latest="${PUBLISH_AS_LATEST}" \
-f monorepo_tag="${TAG}"
- echo "### Site Deployment" >> $GITHUB_STEP_SUMMARY
- echo "Triggered deploy-site.yml on github/copilot-sdk-java for version ${VERSION}" >> $GITHUB_STEP_SUMMARY
+ {
+ echo "### Site Deployment"
+ echo "Triggered deploy-site.yml on github/copilot-sdk-java for version ${VERSION}"
+ } >> "$GITHUB_STEP_SUMMARY"
env:
GITHUB_TOKEN: ${{ secrets.JAVA_RELEASE_GITHUB_TOKEN }}
diff --git a/.github/workflows/java-publish-snapshot.yml b/.github/workflows/java-publish-snapshot.yml
index 8c957627ff..c0a894dca0 100644
--- a/.github/workflows/java-publish-snapshot.yml
+++ b/.github/workflows/java-publish-snapshot.yml
@@ -16,8 +16,282 @@ concurrency:
cancel-in-progress: false
jobs:
- publish-snapshot:
+ resolve-source:
+ name: Resolve immutable snapshot source
+ runs-on: ubuntu-latest
+ outputs:
+ source_sha: ${{ steps.source.outputs.sha }}
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ github.sha }}
+ fetch-depth: 1
+ persist-credentials: false
+
+ - id: source
+ shell: bash
+ run: |
+ SHA=$(git rev-parse HEAD)
+ echo "sha=$SHA" >> "$GITHUB_OUTPUT"
+
+ build-linux-arm64-classifier:
+ name: Build Linux 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-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate linux-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
+ node copilot-native/scripts/validate-native-host.mjs linux-arm64
+ mvn -B -pl copilot-native package -DskipTests -Dcopilot.native.libc=glibc
+ VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
+ JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-linux-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 linux-arm64 "$JAR" "$(basename "$JAR")" ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder "$PRIMARY_JAR"
+ MANIFEST="copilot-native/target/linux-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-linux-arm64-snapshot-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-linux-arm64.jar
+ java/copilot-native/target/linux-arm64-${{ steps.build.outputs.version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ build-windows-classifier:
+ name: Build Windows snapshot classifier
+ needs: resolve-source
+ runs-on: windows-latest
+ permissions:
+ contents: read
+ outputs:
+ version: ${{ steps.build.outputs.version }}
+ defaults:
+ run:
+ shell: pwsh
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ github.sha }}
+ fetch-depth: 1
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate win32-x64 classifier
+ id: build
+ run: |
+ $sourceCommit = git rev-parse HEAD
+ if ($sourceCommit -ne '${{ needs.resolve-source.outputs.source_sha }}') {
+ throw "Checked out $sourceCommit instead of the resolved snapshot source."
+ }
+ node copilot-native/scripts/validate-native-host.mjs win32-x64
+ mvn -B -pl copilot-native package -DskipTests
+ $version = mvn help:evaluate "-Dexpression=project.version" -q "-DforceStdout"
+ $jar = "copilot-native/target/copilot-sdk-java-runtime-$version-win32-x64.jar"
+ $primaryJar = "copilot-native/target/copilot-sdk-java-runtime-$version.jar"
+ if (-not (Test-Path -LiteralPath $jar -PathType Leaf)) {
+ throw "Expected Windows classifier was not produced: $jar"
+ }
+ node copilot-native/scripts/validate-native-artifact.mjs classifier win32-x64 $jar ([IO.Path]::GetFileName($jar)) ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder $primaryJar
+ $manifest = "copilot-native/target/win32-x64-$version.sha256"
+ $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $jar).Hash.ToLowerInvariant()
+ "$hash $([IO.Path]::GetFileName($jar))" | Set-Content -NoNewline -Encoding ascii $manifest
+ node copilot-native/scripts/validate-native-artifact.mjs checksum $jar $manifest ([IO.Path]::GetFileName($jar))
+ "version=$version" | Add-Content $env:GITHUB_OUTPUT
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-win32-x64-snapshot-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-win32-x64.jar
+ java/copilot-native/target/win32-x64-${{ steps.build.outputs.version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ build-windows-arm64-classifier:
+ name: Build Windows ARM64 snapshot classifier
+ needs: resolve-source
+ runs-on: windows-11-arm
+ permissions:
+ contents: read
+ outputs:
+ version: ${{ steps.build.outputs.version }}
+ defaults:
+ run:
+ shell: pwsh
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ github.sha }}
+ fetch-depth: 1
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate win32-arm64 classifier
+ id: build
+ run: |
+ $sourceCommit = git rev-parse HEAD
+ if ($sourceCommit -ne '${{ needs.resolve-source.outputs.source_sha }}') {
+ throw "Checked out $sourceCommit instead of the resolved snapshot source."
+ }
+ node copilot-native/scripts/validate-native-host.mjs win32-arm64
+ mvn -B -pl copilot-native package -DskipTests
+ $version = mvn help:evaluate "-Dexpression=project.version" -q "-DforceStdout"
+ $jar = "copilot-native/target/copilot-sdk-java-runtime-$version-win32-arm64.jar"
+ $primaryJar = "copilot-native/target/copilot-sdk-java-runtime-$version.jar"
+ if (-not (Test-Path -LiteralPath $jar -PathType Leaf)) {
+ throw "Expected Windows ARM64 classifier was not produced: $jar"
+ }
+ node copilot-native/scripts/validate-native-artifact.mjs classifier win32-arm64 $jar ([IO.Path]::GetFileName($jar)) ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder $primaryJar
+ $manifest = "copilot-native/target/win32-arm64-$version.sha256"
+ $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $jar).Hash.ToLowerInvariant()
+ "$hash $([IO.Path]::GetFileName($jar))" | Set-Content -NoNewline -Encoding ascii $manifest
+ node copilot-native/scripts/validate-native-artifact.mjs checksum $jar $manifest ([IO.Path]::GetFileName($jar))
+ "version=$version" | Add-Content $env:GITHUB_OUTPUT
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-win32-arm64-snapshot-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-win32-arm64.jar
+ java/copilot-native/target/win32-arm64-${{ steps.build.outputs.version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ build-darwin-classifier:
+ name: Build Darwin snapshot classifier
+ needs: resolve-source
+ runs-on: macos-26
+ 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-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate darwin-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
+ node copilot-native/scripts/validate-native-host.mjs darwin-arm64
+ mvn -B -pl copilot-native package -DskipTests
+ VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
+ JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-darwin-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 darwin-arm64 "$JAR" "$(basename "$JAR")" ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder "$PRIMARY_JAR"
+ MANIFEST="copilot-native/target/darwin-arm64-$VERSION.sha256"
+ HASH=$(shasum -a 256 "$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-darwin-arm64-snapshot-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-darwin-arm64.jar
+ java/copilot-native/target/darwin-arm64-${{ steps.build.outputs.version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ deploy-snapshot:
name: Publish SNAPSHOT to Maven Central
+ needs:
+ [
+ resolve-source,
+ build-linux-arm64-classifier,
+ build-windows-classifier,
+ build-windows-arm64-classifier,
+ build-darwin-classifier,
+ ]
runs-on: ubuntu-latest
defaults:
run:
@@ -26,12 +300,13 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
- fetch-depth: 0
+ ref: ${{ github.sha }}
+ fetch-depth: 1
+ persist-credentials: false
- uses: ./.github/actions/setup-copilot
- - name: Set up JDK 25
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
with:
java-version: "25"
distribution: "microsoft"
@@ -40,22 +315,182 @@ jobs:
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
- - name: Verify version is a SNAPSHOT
- working-directory: ./java
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-linux-arm64-snapshot-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/java-native-linux-arm64
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-win32-x64-snapshot-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/java-native-win32-x64
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-win32-arm64-snapshot-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/java-native-win32-arm64
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-darwin-arm64-snapshot-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ runner.temp }}/java-native-darwin-arm64
+
+ - name: Verify version, source, and Linux ARM64 classifier
+ id: linux-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=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
- echo "Publishing version: $VERSION"
if [[ "$VERSION" != *"-SNAPSHOT" ]]; then
- echo "ERROR: This workflow only publishes SNAPSHOT versions. Current version: $VERSION"
+ echo "::error::This workflow only publishes SNAPSHOT versions. Current version: $VERSION"
+ exit 1
+ fi
+ if [ "$VERSION" != "${{ needs.build-linux-arm64-classifier.outputs.version }}" ]; then
+ echo "::error::Linux ARM64 classifier version does not match deploy version."
exit 1
fi
- echo "### Snapshot Publish" >> $GITHUB_STEP_SUMMARY
- echo "- **Version:** $VERSION" >> $GITHUB_STEP_SUMMARY
- echo "- **Repository:** Maven Central Snapshots" >> $GITHUB_STEP_SUMMARY
+ ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-linux-arm64"
+ JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-linux-arm64.jar"
+ MANIFEST="$ARTIFACT_DIRECTORY/linux-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 linux-arm64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE"
+ echo "linux_arm64_jar=$JAR" >> "$GITHUB_OUTPUT"
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
+ echo "linux_arm64_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT"
- - name: Deploy Snapshot
- working-directory: ./java
- run: mvn -B deploy -DskipTests
+ - name: Verify version, source, and Windows classifier
+ id: windows-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-windows-classifier.outputs.version }}" ]; then
+ echo "::error::Windows classifier version does not match deploy version."
+ exit 1
+ fi
+ ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-win32-x64"
+ JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-win32-x64.jar"
+ MANIFEST="$ARTIFACT_DIRECTORY/win32-x64-$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 win32-x64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE"
+ echo "windows_jar=$JAR" >> "$GITHUB_OUTPUT"
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
+ echo "windows_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT"
+
+ - name: Verify version, source, and Darwin classifier
+ id: darwin-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.windows-artifact.outputs.version }}"
+ if [ "$VERSION" != "${{ needs.build-darwin-classifier.outputs.version }}" ]; then
+ echo "::error::Darwin classifier version does not match deploy version."
+ exit 1
+ fi
+ ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-darwin-arm64"
+ JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-darwin-arm64.jar"
+ MANIFEST="$ARTIFACT_DIRECTORY/darwin-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 darwin-arm64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE"
+ echo "darwin_jar=$JAR" >> "$GITHUB_OUTPUT"
+ echo "darwin_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT"
+
+ - name: Verify version, source, and Windows ARM64 classifier
+ id: windows-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.windows-artifact.outputs.version }}"
+ if [ "$VERSION" != "${{ needs.build-windows-arm64-classifier.outputs.version }}" ]; then
+ echo "::error::Windows ARM64 classifier version does not match deploy version."
+ exit 1
+ fi
+ ARTIFACT_DIRECTORY="${{ runner.temp }}/java-native-win32-arm64"
+ JAR="$ARTIFACT_DIRECTORY/copilot-sdk-java-runtime-$VERSION-win32-arm64.jar"
+ MANIFEST="$ARTIFACT_DIRECTORY/win32-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 win32-arm64 "$JAR" "$(basename "$JAR")" "$GITHUB_WORKSPACE"
+ echo "windows_arm64_jar=$JAR" >> "$GITHUB_OUTPUT"
+ echo "windows_arm64_sha=$(cut -d ' ' -f 1 "$MANIFEST")" >> "$GITHUB_OUTPUT"
+
+ - name: Build Linux classifier and deploy complete snapshot
+ run: |
+ VERSION="${{ steps.windows-artifact.outputs.version }}"
+ mvn -B deploy -DskipTests -Dcopilot.native.libc=glibc \
+ "-Dcopilot.native.external.linux.arm64.classifier.path=${{ steps.linux-arm64-artifact.outputs.linux_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 }}"
+ LINUX_JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-linux-x64.jar"
+ test -f "$LINUX_JAR"
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ classifier linux-x64 "$LINUX_JAR" "$(basename "$LINUX_JAR")" ..
+ LINUX_SHA=$(sha256sum "$LINUX_JAR" | cut -d ' ' -f 1)
+ GROUP_ID=$(mvn -q -pl copilot-native help:evaluate -Dexpression=project.groupId -DforceStdout)
+ ARTIFACT_ID=$(mvn -q -pl copilot-native help:evaluate -Dexpression=project.artifactId -DforceStdout)
+ POM_VERSION=$(mvn -q -pl copilot-native help:evaluate -Dexpression=project.version -DforceStdout)
+ if [ -z "$GROUP_ID" ] || [ -z "$ARTIFACT_ID" ] || [ "$POM_VERSION" != "$VERSION" ]; then
+ echo "::error::Unexpected copilot-native Maven coordinates: $GROUP_ID:$ARTIFACT_ID:$POM_VERSION (expected version $VERSION)"
+ exit 1
+ fi
+ {
+ echo "### Snapshot Publish"
+ echo "- **Version:** $VERSION"
+ echo "- **Source commit:** \`${{ needs.resolve-source.outputs.source_sha }}\`"
+ echo "- **Repository:** Maven Central Snapshots"
+ echo ""
+ echo "#### Maven Coordinates"
+ echo ""
+ echo '```xml'
+ echo ""
+ echo " $GROUP_ID"
+ echo " $ARTIFACT_ID"
+ echo " $POM_VERSION"
+ echo ""
+ echo '```'
+ echo ""
+ echo "#### Published Native Classifiers"
+ echo ""
+ echo "| Classifier | Build runner | Artifact | SHA-256 | Status |"
+ echo "| --- | --- | --- | --- | --- |"
+ 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 "| \`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-arm64\` | \`macos-26\` | \`$(basename "${{ steps.darwin-artifact.outputs.darwin_jar }}")\` | \`${{ steps.darwin-artifact.outputs.darwin_sha }}\` | Published |"
+ } >> "$GITHUB_STEP_SUMMARY"
env:
MAVEN_USERNAME: ${{ secrets.JAVA_MAVEN_CENTRAL_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.JAVA_MAVEN_CENTRAL_PASSWORD }}
diff --git a/.github/workflows/java-sdk-tests.yml b/.github/workflows/java-sdk-tests.yml
index bd0a34bd25..36310d26b5 100644
--- a/.github/workflows/java-sdk-tests.yml
+++ b/.github/workflows/java-sdk-tests.yml
@@ -18,12 +18,26 @@ permissions:
jobs:
java-sdk-inprocess:
- name: "Java SDK InProcess Tests"
+ name: "Java SDK InProcess Tests (${{ matrix.classifier }})"
if: github.event.repository.fork == false
- runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-latest
+ classifier: linux-x64
+ - os: ubuntu-24.04-arm
+ classifier: linux-arm64
+ maven-args: -Dcopilot.native.libc=glibc
+ - os: windows-latest
+ classifier: win32-x64
+ - os: windows-11-arm
+ classifier: win32-arm64
+ - os: macos-26
+ classifier: darwin-arm64
+ runs-on: ${{ matrix.os }}
defaults:
run:
- shell: bash
working-directory: ./java
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -40,10 +54,13 @@ jobs:
with:
node-version: 22
+ - name: Validate native host
+ run: node copilot-native/scripts/validate-native-host.mjs ${{ matrix.classifier }}
+
- name: Run Java SDK tests (InProcess)
env:
CI: "true"
- run: mvn clean verify -Pinprocess
+ run: mvn clean verify -Pinprocess ${{ matrix.maven-args }}
- name: Generate Test Report Summary
if: always()
@@ -55,13 +72,352 @@ jobs:
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
- name: java-test-results-inprocess
+ name: java-test-results-inprocess-${{ matrix.classifier }}
path: |
java/sdk/target/surefire-reports/
java/sdk/target/surefire-reports-isolated/
java/sdk/target/failsafe-reports/
retention-days: 7
+ java-native-publication-linux-arm64:
+ name: "Java Native Publication Input (linux-arm64)"
+ if: github.event.repository.fork == false
+ runs-on: ubuntu-24.04-arm
+ permissions:
+ contents: read
+ outputs:
+ source_sha: ${{ steps.build.outputs.source_sha }}
+ version: ${{ steps.build.outputs.version }}
+ defaults:
+ run:
+ shell: bash
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate linux-arm64 classifier
+ id: build
+ run: |
+ set -euo pipefail
+ node copilot-native/scripts/validate-native-host.mjs linux-arm64
+ mvn -B -pl copilot-native package -DskipTests -Dcopilot.native.libc=glibc
+ VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
+ JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-linux-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 linux-arm64 "$JAR" "$(basename "$JAR")" ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder "$PRIMARY_JAR"
+ MANIFEST="copilot-native/target/linux-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 "source_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-publication-linux-arm64-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-linux-arm64.jar
+ java/copilot-native/target/linux-arm64-${{ steps.build.outputs.version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ java-native-publication-windows:
+ name: "Java Native Publication Input (win32-x64)"
+ if: github.event.repository.fork == false
+ runs-on: windows-latest
+ permissions:
+ contents: read
+ outputs:
+ source_sha: ${{ steps.build.outputs.source_sha }}
+ version: ${{ steps.build.outputs.version }}
+ defaults:
+ run:
+ shell: pwsh
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate win32-x64 classifier
+ id: build
+ run: |
+ node copilot-native/scripts/validate-native-host.mjs win32-x64
+ mvn -B -pl copilot-native package -DskipTests
+ $version = mvn help:evaluate "-Dexpression=project.version" -q "-DforceStdout"
+ $jar = "copilot-native/target/copilot-sdk-java-runtime-$version-win32-x64.jar"
+ $primaryJar = "copilot-native/target/copilot-sdk-java-runtime-$version.jar"
+ if (-not (Test-Path -LiteralPath $jar -PathType Leaf)) {
+ throw "Expected Windows classifier was not produced: $jar"
+ }
+ node copilot-native/scripts/validate-native-artifact.mjs classifier win32-x64 $jar ([IO.Path]::GetFileName($jar)) ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder $primaryJar
+ $manifest = "copilot-native/target/win32-x64-$version.sha256"
+ $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $jar).Hash.ToLowerInvariant()
+ "$hash $([IO.Path]::GetFileName($jar))" | Set-Content -NoNewline -Encoding ascii $manifest
+ node copilot-native/scripts/validate-native-artifact.mjs checksum $jar $manifest ([IO.Path]::GetFileName($jar))
+ echo "source_sha=$(git rev-parse HEAD)" >> $env:GITHUB_OUTPUT
+ echo "version=$version" >> $env:GITHUB_OUTPUT
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-publication-win32-x64-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-win32-x64.jar
+ java/copilot-native/target/win32-x64-${{ steps.build.outputs.version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ java-native-publication-windows-arm64:
+ name: "Java Native Publication Input (win32-arm64)"
+ if: github.event.repository.fork == false
+ runs-on: windows-11-arm
+ permissions:
+ contents: read
+ outputs:
+ source_sha: ${{ steps.build.outputs.source_sha }}
+ version: ${{ steps.build.outputs.version }}
+ defaults:
+ run:
+ shell: pwsh
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate win32-arm64 classifier
+ id: build
+ run: |
+ node copilot-native/scripts/validate-native-host.mjs win32-arm64
+ mvn -B -pl copilot-native package -DskipTests
+ $version = mvn help:evaluate "-Dexpression=project.version" -q "-DforceStdout"
+ $jar = "copilot-native/target/copilot-sdk-java-runtime-$version-win32-arm64.jar"
+ $primaryJar = "copilot-native/target/copilot-sdk-java-runtime-$version.jar"
+ if (-not (Test-Path -LiteralPath $jar -PathType Leaf)) {
+ throw "Expected Windows ARM64 classifier was not produced: $jar"
+ }
+ node copilot-native/scripts/validate-native-artifact.mjs classifier win32-arm64 $jar ([IO.Path]::GetFileName($jar)) ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder $primaryJar
+ $manifest = "copilot-native/target/win32-arm64-$version.sha256"
+ $hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $jar).Hash.ToLowerInvariant()
+ "$hash $([IO.Path]::GetFileName($jar))" | Set-Content -NoNewline -Encoding ascii $manifest
+ node copilot-native/scripts/validate-native-artifact.mjs checksum $jar $manifest ([IO.Path]::GetFileName($jar))
+ echo "source_sha=$(git rev-parse HEAD)" >> $env:GITHUB_OUTPUT
+ echo "version=$version" >> $env:GITHUB_OUTPUT
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-publication-win32-arm64-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-win32-arm64.jar
+ java/copilot-native/target/win32-arm64-${{ steps.build.outputs.version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ java-native-publication-darwin:
+ name: "Java Native Publication Input (darwin-arm64)"
+ if: github.event.repository.fork == false
+ runs-on: macos-26
+ permissions:
+ contents: read
+ outputs:
+ source_sha: ${{ steps.build.outputs.source_sha }}
+ version: ${{ steps.build.outputs.version }}
+ defaults:
+ run:
+ shell: bash
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - name: Build and validate darwin-arm64 classifier
+ id: build
+ run: |
+ set -euo pipefail
+ node copilot-native/scripts/validate-native-host.mjs darwin-arm64
+ mvn -B -pl copilot-native package -DskipTests
+ VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
+ JAR="copilot-native/target/copilot-sdk-java-runtime-$VERSION-darwin-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 darwin-arm64 "$JAR" "$(basename "$JAR")" ..
+ node copilot-native/scripts/validate-native-artifact.mjs placeholder "$PRIMARY_JAR"
+ MANIFEST="copilot-native/target/darwin-arm64-$VERSION.sha256"
+ HASH=$(shasum -a 256 "$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 "source_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
+
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
+ with:
+ name: java-native-publication-darwin-arm64-${{ github.run_id }}-${{ github.run_attempt }}
+ path: |
+ java/copilot-native/target/copilot-sdk-java-runtime-${{ steps.build.outputs.version }}-darwin-arm64.jar
+ java/copilot-native/target/darwin-arm64-${{ steps.build.outputs.version }}.sha256
+ if-no-files-found: error
+ retention-days: 1
+
+ java-native-publication-assembly:
+ name: "Java Native Publication Assembly"
+ if: github.event.repository.fork == false
+ needs:
+ [
+ java-native-publication-linux-arm64,
+ java-native-publication-windows,
+ java-native-publication-windows-arm64,
+ java-native-publication-darwin,
+ ]
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ shell: bash
+ working-directory: ./java
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ github.sha }}
+ persist-credentials: false
+
+ - uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5
+ with:
+ java-version: "25"
+ distribution: "microsoft"
+ cache: "maven"
+
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
+ with:
+ node-version: 22
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-publication-linux-arm64-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ github.workspace }}/java/native-publication-input/linux-arm64
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-publication-win32-x64-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ github.workspace }}/java/native-publication-input/windows
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-publication-win32-arm64-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ github.workspace }}/java/native-publication-input/windows-arm64
+
+ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ with:
+ name: java-native-publication-darwin-arm64-${{ github.run_id }}-${{ github.run_attempt }}
+ path: ${{ github.workspace }}/java/native-publication-input/darwin
+
+ - name: Verify native inputs and deploy the complete local release
+ run: |
+ set -euo pipefail
+ test "$(git rev-parse HEAD)" = "${{ needs.java-native-publication-linux-arm64.outputs.source_sha }}"
+ test "$(git rev-parse HEAD)" = "${{ needs.java-native-publication-windows.outputs.source_sha }}"
+ test "$(git rev-parse HEAD)" = "${{ needs.java-native-publication-windows-arm64.outputs.source_sha }}"
+ test "$(git rev-parse HEAD)" = "${{ needs.java-native-publication-darwin.outputs.source_sha }}"
+ VERSION="${{ needs.java-native-publication-windows.outputs.version }}"
+ test "$VERSION" = "${{ needs.java-native-publication-linux-arm64.outputs.version }}"
+ test "$VERSION" = "${{ needs.java-native-publication-windows-arm64.outputs.version }}"
+ test "$VERSION" = "${{ needs.java-native-publication-darwin.outputs.version }}"
+ LINUX_ARM64_DIRECTORY="$GITHUB_WORKSPACE/java/native-publication-input/linux-arm64"
+ WINDOWS_DIRECTORY="$GITHUB_WORKSPACE/java/native-publication-input/windows"
+ WINDOWS_ARM64_DIRECTORY="$GITHUB_WORKSPACE/java/native-publication-input/windows-arm64"
+ DARWIN_DIRECTORY="$GITHUB_WORKSPACE/java/native-publication-input/darwin"
+ LINUX_ARM64_JAR="$LINUX_ARM64_DIRECTORY/copilot-sdk-java-runtime-$VERSION-linux-arm64.jar"
+ LINUX_ARM64_MANIFEST="$LINUX_ARM64_DIRECTORY/linux-arm64-$VERSION.sha256"
+ WINDOWS_JAR="$WINDOWS_DIRECTORY/copilot-sdk-java-runtime-$VERSION-win32-x64.jar"
+ WINDOWS_MANIFEST="$WINDOWS_DIRECTORY/win32-x64-$VERSION.sha256"
+ WINDOWS_ARM64_JAR="$WINDOWS_ARM64_DIRECTORY/copilot-sdk-java-runtime-$VERSION-win32-arm64.jar"
+ WINDOWS_ARM64_MANIFEST="$WINDOWS_ARM64_DIRECTORY/win32-arm64-$VERSION.sha256"
+ DARWIN_JAR="$DARWIN_DIRECTORY/copilot-sdk-java-runtime-$VERSION-darwin-arm64.jar"
+ DARWIN_MANIFEST="$DARWIN_DIRECTORY/darwin-arm64-$VERSION.sha256"
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ checksum "$LINUX_ARM64_JAR" "$LINUX_ARM64_MANIFEST" "$(basename "$LINUX_ARM64_JAR")"
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ classifier linux-arm64 "$LINUX_ARM64_JAR" "$(basename "$LINUX_ARM64_JAR")" ..
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ checksum "$WINDOWS_JAR" "$WINDOWS_MANIFEST" "$(basename "$WINDOWS_JAR")"
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ classifier win32-x64 "$WINDOWS_JAR" "$(basename "$WINDOWS_JAR")" ..
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ checksum "$WINDOWS_ARM64_JAR" "$WINDOWS_ARM64_MANIFEST" "$(basename "$WINDOWS_ARM64_JAR")"
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ classifier win32-arm64 "$WINDOWS_ARM64_JAR" "$(basename "$WINDOWS_ARM64_JAR")" ..
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ checksum "$DARWIN_JAR" "$DARWIN_MANIFEST" "$(basename "$DARWIN_JAR")"
+ node copilot-native/scripts/validate-native-artifact.mjs \
+ classifier darwin-arm64 "$DARWIN_JAR" "$(basename "$DARWIN_JAR")" ..
+ export GNUPGHOME="$GITHUB_WORKSPACE/java/copilot-native/target/local-publication-gpg"
+ rm -rf "$GNUPGHOME"
+ mkdir -p "$GNUPGHOME"
+ chmod 700 "$GNUPGHOME"
+ gpg --batch --pinentry-mode loopback --passphrase '' \
+ --quick-generate-key 'Copilot SDK local validation ' rsa2048 sign 1d
+ LOCAL_REPOSITORY="$GITHUB_WORKSPACE/java/copilot-native/target/local-publication-repository"
+ rm -rf "$LOCAL_REPOSITORY"
+ mvn -B -pl copilot-native deploy -Prelease -DskipTests \
+ -Dcopilot.native.libc=glibc \
+ -Dcopilot.native.test.local.publication=true \
+ "-Dcopilot.native.external.linux.arm64.classifier.path=$LINUX_ARM64_JAR" \
+ "-Dcopilot.native.external.win32.classifier.path=$WINDOWS_JAR" \
+ "-Dcopilot.native.external.win32.arm64.classifier.path=$WINDOWS_ARM64_JAR" \
+ "-Dcopilot.native.external.darwin.classifier.path=$DARWIN_JAR" \
+ "-Dmaven.repo.local=$LOCAL_REPOSITORY"
+ node copilot-native/scripts/validate-local-publication.mjs \
+ "$LOCAL_REPOSITORY" copilot-sdk-java-runtime "$VERSION" .. --signatures
+
java-sdk:
name: "Java SDK Tests (JDK ${{ matrix.test-jdk }})"
if: github.event.repository.fork == false
@@ -125,7 +481,9 @@ jobs:
if: matrix.test-jdk == '25'
env:
CI: "true"
- run: mvn verify -Dskip.test.harness=true
+ run: |
+ node copilot-native/scripts/validate-native-host.mjs linux-x64
+ mvn verify -Dskip.test.harness=true -Dcopilot.native.libc=glibc -Dcopilot.native.skip.download=false
- name: Switch to JDK 17
if: matrix.test-jdk == '17'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a5974c67d7..d695ed6fb6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,14 @@ See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the fu
## [Unreleased]
+### Feature: rotating session-scoped GitHub credentials
+
+All six SDKs can now acquire short-lived GitHub credentials through a session-scoped callback. The SDK registers the callback before session create or resume, maps `initial` and `refresh` requests to the owning session, and removes registrations on rollback, replacement, session close, and client close. Static per-session `gitHubToken` credentials remain supported and are mutually exclusive with the callback.
+
+Token responses use the shared tagged token/cancelled shape and require `expiresIn`, expressed as the positive number of seconds remaining when the callback completes. See [github/copilot-agent-runtime#16381](https://github.com/github/copilot-agent-runtime/pull/16381) for the runtime credential-authority implementation.
+
+Initial acquisition occurs during create or resume; cancellation, callback errors, and invalid credentials reject that operation instead of falling back to ambient authentication. Idle sessions refresh only before their next credential-consuming operation.
+
### Feature: extensions can request sensitive environment variables
Copilot CLI extensions can now ask for named sensitive environment variables when they join a session. `joinSession()` accepts a `requestedEnvironmentVariables` option listing the variable names the extension needs. The CLI shows a permission prompt naming the extension and the exact variables requested. On approval, only those variables reach that extension and their values are written into the extension process's `process.env` before `joinSession()` resolves. On denial, `joinSession()` rejects, the extension does not load, and its tools never reach the model.
diff --git a/docs/auth/authenticate.md b/docs/auth/authenticate.md
index 2fc80dd0aa..a7582b5510 100644
--- a/docs/auth/authenticate.md
+++ b/docs/auth/authenticate.md
@@ -262,6 +262,131 @@ const client = new CopilotClient({
For more information, see [GitHub OAuth](../setup/github-oauth.md).
+## Rotating session-scoped GitHub tokens
+
+For multi-user services and integrations, set a token provider on each session instead of storing one long-lived token. The runtime calls the provider for the effective GitHub host and identifies the request as `initial` or `refresh`. The session ID is absent only when a cloud session has not received its ID yet.
+
+Return a tagged token result or an explicit cancellation. Every token result must include `expiresIn`: the positive number of seconds remaining when the callback completes. Production GitHub tokens typically last eight hours, so `8 * 60 * 60` is a common value. Do not set both the static per-session token and the provider.
+
+
+TypeScript
+
+
+```typescript
+const session = await client.createSession({
+ gitHubTokenProvider: async ({ host, sessionId, reason }) => {
+ const token = await acquireGitHubToken({ host, sessionId, reason });
+ return {
+ kind: "token",
+ accessToken: token.value,
+ expiresIn: token.secondsRemaining,
+ };
+ },
+});
+```
+
+
+
+Python
+
+
+```python
+async def provide_github_token(args):
+ token = await acquire_github_token(
+ host=args["host"],
+ session_id=args["session_id"],
+ reason=args["reason"],
+ )
+ return {
+ "kind": "token",
+ "accessToken": token.value,
+ "expiresIn": token.seconds_remaining,
+ }
+
+
+session = await client.create_session(github_token_provider=provide_github_token)
+```
+
+
+
+Go
+
+
+```go
+session, err := client.CreateSession(ctx, &copilot.SessionConfig{
+ GitHubTokenProvider: func(args copilot.GitHubTokenProviderArgs) (*copilot.GitHubTokenProviderResult, error) {
+ token, secondsRemaining, err := acquireGitHubToken(args.Host, args.SessionID, args.Reason)
+ if err != nil {
+ return nil, err
+ }
+ return copilot.GitHubTokenResult(&copilot.GitHubToken{
+ AccessToken: token,
+ ExpiresIn: secondsRemaining,
+ }), nil
+ },
+})
+```
+
+
+
+.NET
+
+
+```csharp
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ GitHubTokenProvider = async args =>
+ {
+ var token = await AcquireGitHubTokenAsync(args.Host, args.SessionId, args.Reason);
+ return GitHubTokenProviderResult.FromToken(new GitHubToken
+ {
+ AccessToken = token.Value,
+ ExpiresIn = token.SecondsRemaining,
+ });
+ },
+});
+```
+
+
+
+Java
+
+
+```java
+var session = client.createSession(new SessionConfig()
+ .setGitHubTokenProvider(args ->
+ acquireGitHubToken(args.host(), args.sessionId(), args.reason())
+ .thenApply(token -> GitHubTokenProviderResult.token(
+ token.value(), token.secondsRemaining())))
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+).get();
+```
+
+
+
+Rust
+
+
+```rust
+let provider = Arc::new(|args: GitHubTokenProviderArgs| async move {
+ let token = acquire_github_token(&args.host, args.session_id.as_ref(), args.reason).await?;
+ Ok(GitHubTokenProviderResult::Token(GitHubToken::new(
+ token.value,
+ token.seconds_remaining,
+ )))
+});
+
+let session = client
+ .create_session(SessionConfig::default().with_github_token_provider(provider))
+ .await?;
+```
+
+
+
+The runtime performs the `initial` acquisition as part of session creation or resume. A cancelled acquisition, provider error, invalid response, or token without a stable account identity rejects the create or resume operation. The runtime does not fall back to ambient authentication.
+
+After the session is established, the runtime performs async preflight before each credential-consuming operation. It requests a `refresh` when the current token has one hour or less remaining. Idle sessions are not refreshed until their next credential-consuming operation. The runtime does not use background timers, rejection-driven replay, 401/403 challenge propagation, or upscope for this callback.
+
## Environment variables
For automation, CI/CD pipelines, and server-to-server scenarios, you can authenticate using environment variables.
diff --git a/docs/features/skills.md b/docs/features/skills.md
index 5b8388162a..d76196429e 100644
--- a/docs/features/skills.md
+++ b/docs/features/skills.md
@@ -351,6 +351,22 @@ The markdown body contains the instructions that are injected into the session c
| .NET | `SkillDirectories` | `List` | Directories to load skills from |
| .NET | `DisabledSkills` | `List` | Skills to disable |
+### Built-in skills and `mode: "empty"`
+
+The runtime ships with a set of bundled **built-in** skills that are eligible by
+default. When you run the client in `mode: "empty"` (the recommended baseline for
+[multi-tenant servers](../setup/multi-tenancy.md)), the SDK excludes every
+runtime-bundled built-in skill: it sends an empty `includedBuiltinSkills` list on
+the post-create and post-resume options patch, alongside the empty
+`installedPlugins` list.
+
+This exclusion is the default, not a permanent restriction. To allow selected
+runtime-bundled skills, set `includedBuiltinSkills` (or the language-specific
+casing) to their names. You can also opt into your **own** custom skills under
+`mode: "empty"`—enable skills and pass your own `skillDirectories`—and those
+remain fully usable, including a custom skill that shares a name with a built-in.
+Under `mode: "copilot-cli"` the field is omitted unless you set the option.
+
## Best practices
1. **Organize by domain** - Group related skills together (e.g., `skills/security/`, `skills/testing/`)
diff --git a/docs/setup/bundled-cli.md b/docs/setup/bundled-cli.md
index f067de8fde..9d9c80c35b 100644
--- a/docs/setup/bundled-cli.md
+++ b/docs/setup/bundled-cli.md
@@ -39,6 +39,9 @@ flowchart TB
## Quick start
+> [!NOTE]
+> Each SDK has a minimum language runtime requirement—see the Prerequisites section of the [Node.js](../../nodejs/README.md#prerequisites), [Python](../../python/README.md#prerequisites), [Go](../../go/README.md#prerequisites), [Rust](../../rust/README.md#prerequisites), [Java](../../java/README.md#prerequisites), or [.NET](../../dotnet/README.md#prerequisites) README—since an unsupported runtime (e.g. Python below the stated floor) can cause `pip`/package managers to silently resolve an outdated SDK release instead of reporting a version conflict.
+
Node.js / TypeScript
diff --git a/docs/setup/multi-tenancy.md b/docs/setup/multi-tenancy.md
index 2f82dde0bf..ebbafe4fb0 100644
--- a/docs/setup/multi-tenancy.md
+++ b/docs/setup/multi-tenancy.md
@@ -24,7 +24,9 @@ This guide is a sister to [Scaling and multi-tenancy](./scaling.md). Use that gu
| `baseDirectory` | Isolating `COPILOT_HOME` per runtime instance | Ignored when connecting to an existing runtime. |
| `sessionFs` | Routing session filesystem storage off local disk | Pair with per-session filesystem providers. |
| `RuntimeConnection.forUri(url)` | Sharing one already-running runtime | Language names vary; see samples below. |
-| Per-session `gitHubToken` | Scoping auth to the requesting user | Prefer this over a single shared user token. |
+| Per-session GitHub token or provider | Scoping auth to the requesting user | Prefer a rotating provider for short-lived credentials; use a static `gitHubToken` only when rotation is unnecessary. |
+
+For callback-backed credentials, see [Rotating session-scoped GitHub tokens](../auth/authenticate.md#rotating-session-scoped-github-tokens). Each session owns its provider registration, so concurrent sessions can use different GitHub hosts and accounts without sharing callback state.
### `mode: "empty"`
@@ -396,10 +398,13 @@ Session-level isolation means the runtime keeps user-specific model and state in
| Session state | Per session ID under `COPILOT_HOME/session-state/{sessionId}`. |
| GitHub identity | Per-session when `gitHubToken` is set on the session. |
| Tools | Explicit in `mode: "empty"`; ambient in `mode: "copilot-cli"`. |
+| Skills | In `mode: "empty"` no runtime-bundled built-in skills are eligible by default; callers can allow selected built-ins or opt into their own custom skills. Ambient in `mode: "copilot-cli"`. |
| Host filesystem | Shared by the runtime process if host tools are available. |
`mode: "empty"` is what makes shared runtime patterns viable: no ambient OS tools are exposed unless your application registers or allows them. With `mode: "copilot-cli"`, OS filesystem access is shared through the host process, so do not use that mode for multi-user server mode.
+Under `mode: "empty"` the SDK excludes every runtime-bundled built-in skill by default (it sends an empty `includedBuiltinSkills` list on the post-create/post-resume options patch, alongside the empty `installedPlugins` list). Set `includedBuiltinSkills` (or the language-specific casing) to explicitly allow selected built-ins, just as `availableTools` allows selected runtime-bundled tools. A caller can also opt into its **own** custom skills—for example by enabling skills and pointing at its own skill directories—and those remain usable.
+
Session state is stored under `COPILOT_HOME/session-state/{sessionId}` unless you route it through `sessionFs`. Use unique session IDs that include your own tenant or user boundary, and enforce access control before resuming or deleting sessions.
## Pattern comparison
diff --git a/dotnet/README.md b/dotnet/README.md
index 6efd6e094c..461ff0cf94 100644
--- a/dotnet/README.md
+++ b/dotnet/README.md
@@ -133,6 +133,7 @@ Create a new conversation session.
- `InfiniteSessions` - Configure automatic context compaction (see below)
- `WorkingDirectory` - Working directory for the session. When not set, the runtime uses its own process working directory.
- `EnableSessionStore` - Enables the cross-session store for search and retrieval across sessions. When unset in `CopilotClientMode.CopilotCli`, the runtime default applies (enabled). In `CopilotClientMode.Empty`, defaults to disabled.
+- `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`.
- `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 user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section.
- `Hooks` - Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section.
@@ -144,6 +145,24 @@ Resume an existing session. Returns the session with `WorkspacePath` populated i
**ResumeSessionConfig:**
- `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`.
+
+```csharp
+await using var session = await client.CreateSessionAsync(new SessionConfig
+{
+ GitHubTokenProvider = async args =>
+ {
+ var token = await AcquireTokenAsync(args.Host);
+ return GitHubTokenProviderResult.FromToken(new GitHubToken
+ {
+ AccessToken = token,
+ ExpiresIn = 8 * 60 * 60
+ });
+ }
+});
+```
+
+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.
##### `PingAsync(string? message = null): Task`
diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs
index 86178ba74b..f2da0a48f7 100644
--- a/dotnet/src/Client.cs
+++ b/dotnet/src/Client.cs
@@ -70,6 +70,7 @@ public sealed partial class CopilotClient : IDisposable, IAsyncDisposable
/// that has not been explicitly disposed or removed.
///
internal readonly ConcurrentDictionary _sessions = new();
+ private readonly ConcurrentDictionary>> _gitHubTokenProviders = new();
private readonly CopilotClientOptions _options;
private readonly RuntimeConnection _connection;
@@ -91,8 +92,7 @@ public sealed partial class CopilotClient : IDisposable, IAsyncDisposable
///
/// Client-global RPC handlers (e.g. the LLM inference provider adapter),
- /// built once at construction when the corresponding option is configured and
- /// registered on every connection. Null when no client-global API is enabled.
+ /// built once at construction and registered on every connection.
///
private readonly ClientGlobalApiHandlers? _clientGlobalApis;
@@ -541,6 +541,7 @@ public async Task StopAsync()
}
_sessions.Clear();
+ ClearGitHubTokenProviders();
await CleanupConnectionAsync(errors, gracefulRuntimeShutdown: true);
@@ -572,6 +573,7 @@ public async Task StopAsync()
public async Task ForceStopAsync()
{
_sessions.Clear();
+ ClearGitHubTokenProviders();
var errors = new List();
await CleanupConnectionAsync(errors, gracefulRuntimeShutdown: false);
@@ -1030,8 +1032,9 @@ private void ApplyConfigDefaultsForMode(SessionConfigBase config)
/// patch for the current mode. In empty mode this defaults the four
/// overridable feature flags to safe values (caller values from
/// win); installedPlugins=[] is
- /// unconditional under empty mode so apps that need plugins must switch
- /// modes. In copilot-cli mode only explicitly-set fields are forwarded.
+ /// unconditional under empty mode. includedBuiltinSkills defaults to
+ /// an empty list, but callers can explicitly allow selected runtime-bundled
+ /// skills. In copilot-cli mode only explicitly-set fields are forwarded.
///
private async Task UpdateSessionOptionsForModeAsync(CopilotSession session, SessionConfigBase config, CancellationToken cancellationToken)
{
@@ -1041,6 +1044,7 @@ private async Task UpdateSessionOptionsForModeAsync(CopilotSession session, Sess
bool? coauthorEnabled = null;
bool? manageScheduleEnabled = null;
IList? installedPlugins = null;
+ IList? includedBuiltinSkills = null;
if (_options.Mode == CopilotClientMode.Empty)
{
@@ -1049,6 +1053,7 @@ private async Task UpdateSessionOptionsForModeAsync(CopilotSession session, Sess
coauthorEnabled = config.CoauthorEnabled ?? false;
manageScheduleEnabled = config.ManageScheduleEnabled ?? false;
installedPlugins = [];
+ includedBuiltinSkills = config.IncludedBuiltinSkills ?? [];
hasAnyPatch = true;
}
else
@@ -1057,6 +1062,7 @@ private async Task UpdateSessionOptionsForModeAsync(CopilotSession session, Sess
if (config.CustomAgentsLocalOnly is not null) { customAgentsLocalOnly = config.CustomAgentsLocalOnly; hasAnyPatch = true; }
if (config.CoauthorEnabled is not null) { coauthorEnabled = config.CoauthorEnabled; hasAnyPatch = true; }
if (config.ManageScheduleEnabled is not null) { manageScheduleEnabled = config.ManageScheduleEnabled; hasAnyPatch = true; }
+ if (config.IncludedBuiltinSkills is not null) { includedBuiltinSkills = config.IncludedBuiltinSkills; hasAnyPatch = true; }
}
if (!hasAnyPatch) return;
@@ -1070,6 +1076,7 @@ await session.Rpc.Options.UpdateAsync(
coauthorEnabled: coauthorEnabled,
manageScheduleEnabled: manageScheduleEnabled,
installedPlugins: installedPlugins,
+ includedBuiltinSkills: includedBuiltinSkills,
cancellationToken: cancellationToken).ConfigureAwait(false);
#pragma warning restore GHCP001
}
@@ -1119,6 +1126,7 @@ await session.Rpc.Options.UpdateAsync(
public async Task CreateSessionAsync(SessionConfig config, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(config);
+ ValidateGitHubTokenConfig(config);
var connection = await EnsureConnectedAsync(cancellationToken);
var totalTimestamp = Stopwatch.GetTimestamp();
@@ -1153,19 +1161,22 @@ public async Task CreateSessionAsync(SessionConfig config, Cance
? null
: (string.IsNullOrEmpty(config.SessionId) ? Guid.NewGuid().ToString() : config.SessionId);
+ var registrationId = RegisterGitHubTokenProvider(config.GitHubTokenProvider);
+ var registrationTransferred = false;
CopilotSession? session = null;
- if (localSessionId != null)
- {
- session = InitializeSession(
- localSessionId,
- connection.Rpc,
- config,
- transformCallbacks,
- hasHooks,
- "CopilotClient.CreateSessionAsync");
- }
try
{
+ if (localSessionId != null)
+ {
+ session = InitializeSession(
+ localSessionId,
+ connection.Rpc,
+ config,
+ transformCallbacks,
+ hasHooks,
+ "CopilotClient.CreateSessionAsync");
+ }
+
var (traceparent, tracestate) = TelemetryHelpers.GetTraceContext();
var request = new CreateSessionRequest(
@@ -1222,6 +1233,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance
Tracestate: tracestate,
ModelCapabilities: config.ModelCapabilities,
GitHubToken: config.GitHubToken,
+ GitHubTokenProviderRegistrationId: registrationId,
RemoteSession: config.RemoteSession,
Cloud: config.Cloud,
InstructionDirectories: config.InstructionDirectories,
@@ -1301,6 +1313,11 @@ public async Task CreateSessionAsync(SessionConfig config, Cance
session.SetOpenCanvases(response.OpenCanvases);
await UpdateSessionOptionsForModeAsync(session, config, cancellationToken).ConfigureAwait(false);
+ if (registrationId is not null)
+ {
+ session.SetGitHubTokenProviderRegistration(registrationId);
+ registrationTransferred = true;
+ }
}
catch (Exception ex)
{
@@ -1316,6 +1333,13 @@ public async Task CreateSessionAsync(SessionConfig config, Cance
throw;
}
+ finally
+ {
+ if (!registrationTransferred && registrationId is not null)
+ {
+ UnregisterGitHubTokenProvider(registrationId);
+ }
+ }
LoggingHelpers.LogTiming(_logger, LogLevel.Debug, null,
"CopilotClient.CreateSessionAsync complete. Elapsed={Elapsed}, SessionId={SessionId}",
@@ -1353,6 +1377,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes
{
ArgumentNullException.ThrowIfNull(sessionId);
ArgumentNullException.ThrowIfNull(config);
+ ValidateGitHubTokenConfig(config);
var connection = await EnsureConnectedAsync(cancellationToken);
var totalTimestamp = Stopwatch.GetTimestamp();
@@ -1375,17 +1400,21 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes
var (wireSystemMessage, transformCallbacks) = ExtractTransformCallbacks(config.SystemMessage);
- // Create and register the session before issuing the RPC so that
- // events emitted by the CLI (e.g. session.start) are not dropped.
- var session = InitializeSession(
- sessionId,
- connection.Rpc,
- config,
- transformCallbacks,
- hasHooks,
- "CopilotClient.ResumeSessionAsync");
+ var registrationId = RegisterGitHubTokenProvider(config.GitHubTokenProvider);
+ var registrationTransferred = false;
+ CopilotSession? session = null;
try
{
+ // Create and register the session before issuing the RPC so that
+ // events emitted by the CLI (e.g. session.start) are not dropped.
+ session = InitializeSession(
+ sessionId,
+ connection.Rpc,
+ config,
+ transformCallbacks,
+ hasHooks,
+ "CopilotClient.ResumeSessionAsync");
+
var (traceparent, tracestate) = TelemetryHelpers.GetTraceContext();
var request = new ResumeSessionRequest(
@@ -1443,6 +1472,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes
Tracestate: tracestate,
ModelCapabilities: config.ModelCapabilities,
GitHubToken: config.GitHubToken,
+ GitHubTokenProviderRegistrationId: registrationId,
RemoteSession: config.RemoteSession,
ContinuePendingWork: config.ContinuePendingWork,
InstructionDirectories: config.InstructionDirectories,
@@ -1486,10 +1516,15 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes
}
await UpdateSessionOptionsForModeAsync(session, config, cancellationToken).ConfigureAwait(false);
+ if (registrationId is not null)
+ {
+ session.SetGitHubTokenProviderRegistration(registrationId);
+ registrationTransferred = true;
+ }
}
catch (Exception ex)
{
- session.RemoveFromClient();
+ session?.RemoveFromClient();
if (ex is not OperationCanceledException)
{
LoggingHelpers.LogTiming(_logger, LogLevel.Warning, ex,
@@ -1499,12 +1534,19 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes
}
throw;
}
+ finally
+ {
+ if (!registrationTransferred && registrationId is not null)
+ {
+ UnregisterGitHubTokenProvider(registrationId);
+ }
+ }
LoggingHelpers.LogTiming(_logger, LogLevel.Debug, null,
"CopilotClient.ResumeSessionAsync complete. Elapsed={Elapsed}, SessionId={SessionId}",
totalTimestamp,
sessionId);
- return session;
+ return session!;
}
///
@@ -1664,7 +1706,10 @@ public async Task DeleteSessionAsync(string sessionId, CancellationToken cancell
throw new InvalidOperationException($"Failed to delete session {sessionId}: {response.Error}");
}
- RemoveSession(sessionId);
+ if (_sessions.TryRemove(sessionId, out var session))
+ {
+ session.ReleaseGitHubTokenProviderRegistration();
+ }
}
///
@@ -1946,25 +1991,94 @@ await Rpc.SessionFs.SetProviderAsync(
///
/// Builds the client-global RPC handler bag at construction time. Registers
/// the LLM inference provider adapter and/or the GitHub telemetry adapter
- /// depending on which options are configured; returns null when no
- /// client-global API is configured so the registration is skipped entirely.
+ /// depending on which options are configured. The GitHub token dispatcher is
+ /// always registered because providers are configured per session.
///
private ClientGlobalApiHandlers? BuildClientGlobalApis()
{
var handler = _options.RequestHandler;
var onGitHubTelemetry = _options.OnGitHubTelemetry;
- if (handler is null && onGitHubTelemetry is null)
- {
- return null;
- }
-
return new ClientGlobalApiHandlers
{
LlmInference = handler is null ? null : new LlmInferenceAdapter(handler, () => _serverRpc),
GitHubTelemetry = onGitHubTelemetry is null ? null : new GitHubTelemetryAdapter(onGitHubTelemetry, _logger),
+ GitHubToken = new GitHubTokenAdapter(this),
};
}
+ private static void ValidateGitHubTokenConfig(SessionConfigBase config)
+ {
+ if (config.GitHubToken is not null && config.GitHubTokenProvider is not null)
+ {
+ throw new ArgumentException(
+ $"{nameof(SessionConfigBase.GitHubToken)} and {nameof(SessionConfigBase.GitHubTokenProvider)} cannot be used together.",
+ nameof(config));
+ }
+ }
+
+ private string? RegisterGitHubTokenProvider(
+ Func>? provider)
+ {
+ if (provider is null)
+ {
+ return null;
+ }
+
+ var registrationId = Guid.NewGuid().ToString();
+ if (!_gitHubTokenProviders.TryAdd(registrationId, provider))
+ {
+ throw new InvalidOperationException("Failed to register GitHub token provider.");
+ }
+ return registrationId;
+ }
+
+ internal void UnregisterGitHubTokenProvider(string registrationId)
+ => _gitHubTokenProviders.TryRemove(registrationId, out _);
+
+ private void ClearGitHubTokenProviders() => _gitHubTokenProviders.Clear();
+
+ private sealed class GitHubTokenAdapter(CopilotClient client) : IGitHubTokenHandler
+ {
+ public async Task GetTokenAsync(
+ GitHubTokenAcquireRequest request,
+ CancellationToken cancellationToken = default)
+ {
+ if (!client._gitHubTokenProviders.TryGetValue(request.RegistrationId, out var provider))
+ {
+ throw new InvalidOperationException(
+ $"Unknown GitHub token provider registration ID '{request.RegistrationId}'.");
+ }
+
+ var reason = request.Reason == GitHubTokenAcquireReason.Initial
+ ? GitHubTokenRequestReason.Initial
+ : request.Reason == GitHubTokenAcquireReason.Refresh
+ ? GitHubTokenRequestReason.Refresh
+ : throw new InvalidOperationException($"Unknown GitHub token request reason '{request.Reason}'.");
+ var result = await provider(new GitHubTokenProviderArgs
+ {
+ Host = request.Host,
+ SessionId = request.SessionId,
+ Reason = reason,
+ }).ConfigureAwait(false);
+
+ if (result is { Cancelled: true })
+ {
+ return new GitHubTokenAcquireResultCancelled();
+ }
+ if (result?.Token is not { } token)
+ {
+ throw new InvalidOperationException(
+ "GitHub token provider returned neither a token nor cancellation.");
+ }
+ return new GitHubTokenAcquireResultToken
+ {
+ AccessToken = token.AccessToken,
+ TokenType = token.TokenType,
+ ExpiresIn = token.ExpiresIn,
+ };
+ }
+ }
+
///
/// Tells the runtime to route its outbound model-layer requests through this
/// client's LLM inference provider. No-op when interception is not configured.
@@ -2542,11 +2656,6 @@ private void RegisterSession(CopilotSession session)
}
}
- private void RemoveSession(string sessionId)
- {
- _sessions.TryRemove(sessionId, out _);
- }
-
///
/// Disposes the synchronously.
///
@@ -2802,6 +2911,7 @@ internal record CreateSessionRequest(
string? Tracestate = null,
ModelCapabilitiesOverride? ModelCapabilities = null,
string? GitHubToken = null,
+ [property: JsonPropertyName("gitHubTokenProviderRegistrationId")] string? GitHubTokenProviderRegistrationId = null,
RemoteSessionMode? RemoteSession = null,
CloudSessionOptions? Cloud = null,
IList? InstructionDirectories = null,
@@ -2917,6 +3027,7 @@ internal record ResumeSessionRequest(
string? Tracestate = null,
ModelCapabilitiesOverride? ModelCapabilities = null,
string? GitHubToken = null,
+ [property: JsonPropertyName("gitHubTokenProviderRegistrationId")] string? GitHubTokenProviderRegistrationId = null,
RemoteSessionMode? RemoteSession = null,
bool? ContinuePendingWork = null,
IList? InstructionDirectories = null,
diff --git a/dotnet/src/Generated/Rpc.cs b/dotnet/src/Generated/Rpc.cs
index 97f9b52762..c97660f369 100644
--- a/dotnet/src/Generated/Rpc.cs
+++ b/dotnet/src/Generated/Rpc.cs
@@ -61,10 +61,35 @@ internal sealed class ConnectResult
public string Version { get; set; } = string.Empty;
}
+/// Identity of the integrating host, declared once on the `server.connect` handshake so telemetry from this connection is attributed to a single, consistent surface. All fields are optional; omit them to keep the default attribution.
+[Experimental(Diagnostics.Experimental)]
+internal sealed class ConnectClientInfo
+{
+ /// Name of the host editor, e.g. `"vscode"`.
+ [JsonPropertyName("editorName")]
+ public string? EditorName { get; set; }
+
+ /// Version of the host editor, e.g. `"1.124.2"`. Ignored unless it looks like a version string.
+ [JsonPropertyName("editorVersion")]
+ public string? EditorVersion { get; set; }
+
+ /// Name of the Copilot extension within the host, e.g. `"copilot-chat"`.
+ [JsonPropertyName("extensionName")]
+ public string? ExtensionName { get; set; }
+
+ /// Version of the Copilot extension within the host, e.g. `"0.54.0"`. Ignored unless it looks like a version string.
+ [JsonPropertyName("extensionVersion")]
+ public string? ExtensionVersion { get; set; }
+}
+
/// Connection-level opt-ins for the `server.connect` handshake. Transport authentication is consumed by the native protocol boundary before dispatch.
[Experimental(Diagnostics.Experimental)]
internal sealed class ConnectRequest
{
+ /// Identity of the integrating host. Optional; omit it to keep the default attribution.
+ [JsonPropertyName("clientInfo")]
+ public ConnectClientInfo? ClientInfo { get; set; }
+
/// Opt this connection in to GitHub telemetry forwarding for its lifetime. When set, the runtime forwards every internal telemetry event it emits — across all sessions, plus sessionless events — to this connection over the `gitHubTelemetry.event` notification. 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.
[JsonPropertyName("enableGitHubTelemetryForwarding")]
public bool? EnableGitHubTelemetryForwarding { get; set; }
@@ -282,6 +307,19 @@ public sealed class ModelCapabilities
public ModelCapabilitiesSupports? Supports { get; set; }
}
+/// A service-published message about a model, carrying a stable machine-readable code alongside human-readable text.
+[Experimental(Diagnostics.Experimental)]
+public sealed class ModelMessage
+{
+ /// Stable machine-readable identifier for the message, such as `client_version_deprecated`. Hosts can key custom presentation off this; unrecognized codes should fall back to displaying `message`.
+ [JsonPropertyName("code")]
+ public string Code { get; set; } = string.Empty;
+
+ /// Human-readable message text intended for display to the user.
+ [JsonPropertyName("message")]
+ public string Message { get; set; } = string.Empty;
+}
+
/// Policy state (if applicable).
[Experimental(Diagnostics.Experimental)]
public sealed class ModelPolicy
@@ -295,6 +333,15 @@ public sealed class ModelPolicy
public string? Terms { get; set; }
}
+/// Service-published warning text that hosts should display when presenting a model.
+[Experimental(Diagnostics.Experimental)]
+public sealed class ModelWarningText
+{
+ /// Data-retention warning for the model. The text may contain Markdown links and should be rendered as Markdown when supported.
+ [JsonPropertyName("dataRetention")]
+ public string? DataRetention { get; set; }
+}
+
/// Copilot model metadata, including identifier, display name, capabilities, policy, billing, reasoning efforts, and picker categories.
[Experimental(Diagnostics.Experimental)]
public sealed class Model
@@ -315,6 +362,10 @@ public sealed class Model
[JsonPropertyName("id")]
public string Id { get; set; } = string.Empty;
+ /// Informational notices the service published for this model, such as an upcoming change or a recommended alternative. Present only when the service published at least one notice. Hosts should surface these without implying anything is wrong with the model.
+ [JsonPropertyName("infoMessages")]
+ public IList? InfoMessages { get; set; }
+
/// Model capability category for grouping in the model picker.
[JsonPropertyName("modelPickerCategory")]
public ModelPickerCategory? ModelPickerCategory { get; set; }
@@ -338,6 +389,14 @@ public sealed class Model
/// Supported reasoning effort levels (only present if model supports reasoning effort).
[JsonPropertyName("supportedReasoningEfforts")]
public IList? SupportedReasoningEfforts { get; set; }
+
+ /// Warnings the service published for this model, such as a deprecated client version. Present only when the service published at least one warning. The model remains usable; hosts should surface these as advisory rather than blocking.
+ [JsonPropertyName("warningMessages")]
+ public IList? WarningMessages { get; set; }
+
+ /// Warning text the service requires hosts to surface for this model. Present only when the service published at least one warning.
+ [JsonPropertyName("warningText")]
+ public ModelWarningText? WarningText { get; set; }
}
/// List of Copilot models available to the resolved user, including capabilities and billing metadata.
@@ -491,6 +550,7 @@ internal sealed class AccountGetQuotaRequest
[JsonDerivedType(typeof(AuthInfoHmac), "hmac")]
[JsonDerivedType(typeof(AuthInfoEnv), "env")]
[JsonDerivedType(typeof(AuthInfoToken), "token")]
+[JsonDerivedType(typeof(AuthInfoTokenProvider), "token-provider")]
[JsonDerivedType(typeof(AuthInfoCopilotApiToken), "copilot-api-token")]
[JsonDerivedType(typeof(AuthInfoUser), "user")]
[JsonDerivedType(typeof(AuthInfoGhCli), "gh-cli")]
@@ -898,11 +958,39 @@ public partial class AuthInfoToken : AuthInfo
[JsonPropertyName("host")]
public required string Host { get; set; }
+ /// Opaque native GitHub credential registration backing this token identity, when applicable.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("registrationId")]
+ public string? RegistrationId { get; set; }
+
/// The token value itself. Treat as a secret.
[JsonPropertyName("token")]
public required string Token { get; set; }
}
+/// Authentication-info variant backed by an SDK GitHub token callback. It carries routing metadata but never a plaintext token.
+/// The token-provider variant of .
+[Experimental(Diagnostics.Experimental)]
+public partial class AuthInfoTokenProvider : AuthInfo
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "token-provider";
+
+ /// Snapshot of the authenticated user's Copilot subscription info, if known.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("copilotUser")]
+ public CopilotUserResponse? CopilotUser { get; set; }
+
+ /// Authentication host.
+ [JsonPropertyName("host")]
+ public required string Host { get; set; }
+
+ /// Opaque SDK callback registration identifier.
+ [JsonPropertyName("registrationId")]
+ public required string RegistrationId { get; set; }
+}
+
/// Authentication-info variant for direct Copilot API token auth sourced from environment variables, with public GitHub host.
/// The copilot-api-token variant of .
[Experimental(Diagnostics.Experimental)]
@@ -2695,6 +2783,10 @@ public sealed class InstalledPluginInfo
[JsonPropertyName("enabled")]
public bool Enabled { get; set; }
+ /// Absolute path of the marketplace directory a live plugin was resolved from. Present only on live, never-persisted records — a plugin belonging to a directory/local marketplace, which is loaded from its real directory on every pass instead of a copy under the installed-plugins cache. Its presence is what marks a listed plugin as live: such a plugin is always present on disk, so `enabled` is its only meaningful state and it is never "not installed".
+ [JsonPropertyName("installedFrom")]
+ public string? InstalledFrom { get; set; }
+
/// Marketplace the plugin came from. Empty string ("") for direct repo / URL / local installs.
[JsonPropertyName("marketplace")]
public string Marketplace { get; set; } = string.Empty;
@@ -4462,6 +4554,10 @@ public sealed class InstalledPlugin
[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;
@@ -5297,6 +5393,32 @@ internal sealed class LogRequest
public string? Url { get; set; }
}
+/// Managed sandbox enforcement state for a session.
+[Experimental(Diagnostics.Experimental)]
+public sealed class SandboxEnforcementStatus
+{
+ /// Whether an enforcement failure has permanently blocked the session.
+ [JsonPropertyName("blocked")]
+ public bool Blocked { get; set; }
+
+ /// The first sandbox enforcement failure that blocked the session.
+ [JsonPropertyName("reason")]
+ public string? Reason { get; set; }
+
+ /// Whether the effective managed policy requires an available sandbox backend.
+ [JsonPropertyName("required")]
+ public bool Required { get; set; }
+}
+
+/// Identifies the target session.
+[Experimental(Diagnostics.Experimental)]
+internal sealed class SessionSandboxGetEnforcementStatusRequest
+{
+ /// Target session identifier.
+ [JsonPropertyName("sessionId")]
+ public string SessionId { get; set; } = string.Empty;
+}
+
/// Authentication status and account metadata for the session.
[Experimental(Diagnostics.Experimental)]
public sealed class SessionAuthStatus
@@ -5350,13 +5472,204 @@ public sealed class SessionSetCredentialsResult
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(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
+{
+ /// The type discriminator.
+ [JsonPropertyName("type")]
+ public virtual string Type { get; set; } = string.Empty;
+}
+
+
+/// Authentication-info input variant for GitHub-internal HMAC auth, carrying the public GitHub host and HMAC secret.
+/// The hmac variant of .
+[Experimental(Diagnostics.Experimental)]
+public partial class SettableAuthInfoHmac : SettableAuthInfo
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "hmac";
+
+ /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("copilotUser")]
+ public CopilotUserResponse? CopilotUser { get; set; }
+
+ /// HMAC secret used to sign requests.
+ [JsonPropertyName("hmac")]
+ public required string Hmac { get; set; }
+
+ /// Authentication host. HMAC auth always targets the public GitHub host.
+ [JsonPropertyName("host")]
+ public required string Host { get; set; }
+}
+
+/// 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(Diagnostics.Experimental)]
+public partial class SettableAuthInfoEnv : SettableAuthInfo
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "env";
+
+ /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("copilotUser")]
+ public CopilotUserResponse? CopilotUser { get; set; }
+
+ /// Name of the environment variable the token was sourced from.
+ [JsonPropertyName("envVar")]
+ public required string EnvVar { get; set; }
+
+ /// Authentication host (e.g. https://github.com or a GHES host).
+ [JsonPropertyName("host")]
+ public required string Host { get; set; }
+
+ /// User login associated with the token. Undefined for server-to-server tokens (those starting with `ghs_`).
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("login")]
+ public string? Login { get; set; }
+
+ /// The token value itself. Treat as a secret.
+ [JsonPropertyName("token")]
+ public required string Token { get; set; }
+}
+
+/// Token authentication accepted by session.gitHubAuth.setCredentials.
+/// The token variant of .
+[Experimental(Diagnostics.Experimental)]
+public partial class SettableAuthInfoToken : SettableAuthInfo
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "token";
+
+ /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("copilotUser")]
+ public CopilotUserResponse? CopilotUser { get; set; }
+
+ /// Authentication host.
+ [JsonPropertyName("host")]
+ public required string Host { get; set; }
+
+ /// The token value itself. Treat as a secret.
+ [JsonPropertyName("token")]
+ public required string Token { get; set; }
+}
+
+/// Authentication-info variant for direct Copilot API token auth sourced from environment variables, with public GitHub host.
+/// The copilot-api-token variant of .
+[Experimental(Diagnostics.Experimental)]
+public partial class SettableAuthInfoCopilotApiToken : SettableAuthInfo
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "copilot-api-token";
+
+ /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("copilotUser")]
+ public CopilotUserResponse? CopilotUser { get; set; }
+
+ /// Authentication host (always the public GitHub host).
+ [JsonPropertyName("host")]
+ public required string Host { get; set; }
+}
+
+/// Authentication-info variant for OAuth user auth, with host and login; the token remains in the runtime secret store.
+/// The user variant of .
+[Experimental(Diagnostics.Experimental)]
+public partial class SettableAuthInfoUser : SettableAuthInfo
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "user";
+
+ /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("copilotUser")]
+ public CopilotUserResponse? CopilotUser { get; set; }
+
+ /// Authentication host.
+ [JsonPropertyName("host")]
+ public required string Host { get; set; }
+
+ /// OAuth user login.
+ [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(Diagnostics.Experimental)]
+public partial class SettableAuthInfoGhCli : SettableAuthInfo
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "gh-cli";
+
+ /// Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this verbatim and does not re-fetch when set.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("copilotUser")]
+ public CopilotUserResponse? CopilotUser { get; set; }
+
+ /// Authentication host.
+ [JsonPropertyName("host")]
+ public required string Host { get; set; }
+
+ /// User login as reported by `gh auth status`.
+ [JsonPropertyName("login")]
+ public required string Login { get; set; }
+
+ /// The token returned by `gh auth token`. Treat as a secret.
+ [JsonPropertyName("token")]
+ public required string Token { get; set; }
+}
+
+/// 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(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; }
+}
+
/// New auth credentials to install on the session. Omit to leave credentials unchanged.
[Experimental(Diagnostics.Experimental)]
internal sealed class SessionSetCredentialsParams
{
/// The new auth credentials to install on the session. When omitted or `undefined`, the call is a no-op and the session's existing credentials are preserved. The runtime installs the supplied value immediately for outbound model/API requests. When the credential carries a raw token (`token`, `env`, or `gh-cli`) but no `copilotUser`, the runtime additionally re-resolves `copilotUser` server-side (best-effort, asynchronously, after the synchronous install) so plan/quota/billing metadata regains fidelity; on resolution failure the verbatim credential remains installed. It does NOT otherwise validate the credential. Several variants carry secret material; treat this method's params as containing secrets at rest and in transit.
[JsonPropertyName("credentials")]
- public AuthInfo? Credentials { get; set; }
+ public SettableAuthInfo? Credentials { get; set; }
/// Target session identifier.
[JsonPropertyName("sessionId")]
@@ -5383,6 +5696,10 @@ public sealed class AuthIdentity
[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; }
@@ -6074,6 +6391,14 @@ public sealed class RunOptions
[JsonPropertyName("limits")]
public FactoryRunLimits? Limits { get; set; }
+ /// Whether to emit factory phase names to the session transcript.
+ [JsonPropertyName("logPhaseNames")]
+ public bool? LogPhaseNames { get; set; }
+
+ /// Whether to notify the originating session when the factory completes.
+ [JsonPropertyName("notifyOnComplete")]
+ public bool? NotifyOnComplete { get; set; }
+
/// Run identifier whose journal and progress should seed this resumed run.
[JsonPropertyName("resumeFromRunId")]
public string? ResumeFromRunId { get; set; }
@@ -6121,6 +6446,14 @@ internal sealed class FactoryResumeRequest
[JsonPropertyName("limits")]
public FactoryRunLimits? Limits { get; set; }
+ /// Whether to emit factory phase names to the session transcript.
+ [JsonPropertyName("logPhaseNames")]
+ public bool? LogPhaseNames { get; set; }
+
+ /// Whether to notify the originating session when the factory completes.
+ [JsonPropertyName("notifyOnComplete")]
+ public bool? NotifyOnComplete { get; set; }
+
/// Factory run identifier.
[JsonPropertyName("runId")]
public string RunId { get; set; } = string.Empty;
@@ -6130,6 +6463,65 @@ internal sealed class FactoryResumeRequest
public string SessionId { get; set; } = string.Empty;
}
+/// Options for an internal tool-originated factory invocation.
+[Experimental(Diagnostics.Experimental)]
+internal sealed class FactoryToolRunOptions
+{
+ /// Per-invocation resource ceiling overrides.
+ [JsonPropertyName("limits")]
+ public FactoryRunLimits? Limits { get; set; }
+
+ /// Run identifier whose journal and progress should seed this resumed run.
+ [JsonPropertyName("resumeFromRunId")]
+ public string? ResumeFromRunId { get; set; }
+}
+
+/// Internal parameters for invoking a registered factory from a tool.
+[Experimental(Diagnostics.Experimental)]
+internal sealed class FactoryToolRunRequest
+{
+ /// Factory input value.
+ [JsonPropertyName("args")]
+ public JsonElement Args { get; set; }
+
+ /// Registered factory name.
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ /// Tool-originated factory invocation options.
+ [JsonPropertyName("options")]
+ public FactoryToolRunOptions? Options { get; set; }
+
+ /// Target session identifier.
+ [JsonPropertyName("sessionId")]
+ public string SessionId { get; set; } = string.Empty;
+
+ /// Opaque identifier of the originating tool call.
+ [JsonPropertyName("toolCallId")]
+ public string? ToolCallId { get; set; }
+}
+
+/// Internal parameters for resuming a factory run from a tool.
+[Experimental(Diagnostics.Experimental)]
+internal sealed class FactoryToolResumeRequest
+{
+ /// Optional per-invocation resource ceiling overrides.
+ [JsonPropertyName("limits")]
+ public FactoryRunLimits? Limits { get; set; }
+
+ /// Factory run identifier.
+ [JsonPropertyName("runId")]
+ public string RunId { get; set; } = string.Empty;
+
+ /// Target session identifier.
+ [JsonPropertyName("sessionId")]
+ public string SessionId { get; set; } = string.Empty;
+
+ /// Opaque identifier of the originating tool call.
+ [JsonPropertyName("toolCallId")]
+ public string? ToolCallId { get; set; }
+}
+
/// Parameters for retrieving a factory run.
[Experimental(Diagnostics.Experimental)]
internal sealed class FactoryGetRunRequest
@@ -9384,7 +9776,7 @@ public partial class McpOauthPendingRequestResponseToken : McpOauthPendingReques
[JsonPropertyName("expiresIn")]
public long? ExpiresIn { get; set; }
- /// OAuth token type. Defaults to Bearer when omitted.
+ /// OAuth token type. Defaults to bearer when omitted.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("tokenType")]
public string? TokenType { get; set; }
@@ -10567,6 +10959,10 @@ public sealed class OptionsUpdateAdditionalContentExclusionPolicy
[Experimental(Diagnostics.Experimental)]
public sealed class CapiSessionOptions
{
+ /// Routing preference used when the session model is `auto`. The runtime persists the preference across cold resume. When omitted, the default routing behavior is used. Resuming an already-resident session cannot change its 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; }
@@ -10588,6 +10984,10 @@ public sealed class SessionInstalledPlugin
[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;
@@ -10802,7 +11202,7 @@ public sealed class SandboxConfig
[JsonPropertyName("addCurrentWorkingDirectory")]
public bool? AddCurrentWorkingDirectory { get; set; }
- /// Whether to auto-grant read access to the tool directories discovered on PATH and in toolchain environment variables (GOROOT, CARGO_HOME, JAVA_HOME, VIRTUAL_ENV, and similar), and to common developer-tool caches, registries, and toolchains in their default home locations (cargo, go, npm, Maven, and more), plus read-write access to (and, on Unix, up-front creation of) the scratch caches builds write on every run (go-build, ccache, sccache, Gradle caches, Cargo lock/tracker files), so builds work without extra configuration; a relocated CARGO_HOME additionally gets its Cargo lock files granted read-write. Set to false to disable every grant listed above: user-installed toolchains (rustup, nvm, pyenv, conda, pipx) then need explicit userPolicy.filesystem entries — readonlyPaths to read them, plus readwriteFiles for a relocated CARGO_HOME's .package-cache and .global-cache, which Cargo locks on every build. Only these developer-tool grants are affected: the working directory (see addCurrentWorkingDirectory), temporary storage, session log paths, and system locations follow their own rules and stay granted, so commands still run. Default: true (enabled by default; set to false to opt out).
+ /// 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; }
@@ -11035,6 +11435,10 @@ internal sealed class SessionUpdateOptionsParams
[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; }
@@ -11095,6 +11499,11 @@ internal sealed class SessionUpdateOptionsParams
[JsonPropertyName("sandboxConfig")]
public SandboxConfig? SandboxConfig { get; set; }
+ /// Origin of the sandbox choice. The runtime uses this only for internal telemetry provenance; managed policy is derived independently.
+ [JsonInclude]
+ [JsonPropertyName("sandboxConfigSource")]
+ internal SandboxConfigSource? SandboxConfigSource { get; set; }
+
/// Replaces the session's capability set with the given list. Use to enable or disable capabilities mid-session (e.g., remove `memory` for reproducible scripted runs). Omit the field to leave the existing capability set unchanged.
[JsonPropertyName("sessionCapabilities")]
public IList? SessionCapabilities { get; set; }
@@ -11918,10 +12327,6 @@ internal sealed class ToolsGetBuiltinDescriptorsRequest
[JsonPropertyName("includeAuthor")]
public bool? IncludeAuthor { get; set; }
- /// Whether line numbers should be omitted from the view tool descriptor.
- [JsonPropertyName("noViewLineNumbers")]
- public bool? NoViewLineNumbers { get; set; }
-
/// Whether descriptors should favor fewer user-intervention prompts.
[JsonPropertyName("reduceUserIntervention")]
public bool? ReduceUserIntervention { get; set; }
@@ -11930,10 +12335,6 @@ internal sealed class ToolsGetBuiltinDescriptorsRequest
[JsonPropertyName("sessionId")]
public string SessionId { get; set; } = string.Empty;
- /// Whether shell commands may only run asynchronously.
- [JsonPropertyName("shellAsyncOnlyEnabled")]
- public bool? ShellAsyncOnlyEnabled { get; set; }
-
/// Shell-specific names and description lines for shell tools.
[JsonPropertyName("shellConfig")]
public ToolsShellDescriptorConfig? ShellConfig { get; set; }
@@ -12079,6 +12480,11 @@ public partial class ExternalToolTextResultForLlmContentShellExit : ExternalTool
[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")]
@@ -13019,6 +13425,10 @@ internal sealed class EnqueueCommandParams
[JsonPropertyName("command")]
public string Command { get; set; } = string.Empty;
+ /// Optional user-facing text for the queue row. The command string is shown when omitted.
+ [JsonPropertyName("displayText")]
+ public string? DisplayText { get; set; }
+
/// Target session identifier.
[JsonPropertyName("sessionId")]
public string SessionId { get; set; } = string.Empty;
@@ -13485,7 +13895,7 @@ public sealed class PermissionsConfigureAdditionalContentExclusionPolicy
[Experimental(Diagnostics.Experimental)]
public sealed class PermissionPathsConfig
{
- /// Additional directories to allow tool access to (in addition to the session's working directory). When `unrestricted` is true, these are still pre-populated on the UnrestrictedPathManager so they remain visible via getDirectories() (e.g. for @-mention completion).
+ /// 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; }
@@ -13578,6 +13988,10 @@ public sealed class PermissionDecisionContext
[JsonPropertyName("outcome")]
public PermissionDecisionOutcome Outcome { get; set; }
+ /// Whether the responding client could ask a user interactively, was running headlessly, or had no response path. Omit when the client cannot determine this authoritatively.
+ [JsonPropertyName("responseCapability")]
+ public PermissionResponseCapability? ResponseCapability { get; set; }
+
/// Controlled reason or actor responsible for the response.
[JsonPropertyName("source")]
public PermissionDecisionSource Source { get; set; }
@@ -14466,7 +14880,7 @@ public sealed class PermissionsPathsAddResult
[Experimental(Diagnostics.Experimental)]
internal sealed class PermissionPathsAddParams
{
- /// Directory to add to the allow-list. The runtime resolves and validates the path before adding.
+ /// 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;
@@ -16233,6 +16647,10 @@ public sealed class QueuePendingItems
[Experimental(Diagnostics.Experimental)]
public sealed class QueuePendingItemsResult
{
+ /// 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; }
@@ -18214,6 +18632,14 @@ public sealed class GitHubTelemetryClientInfo
[JsonPropertyName("copilot_plan")]
public string? CopilotPlan { get; set; }
+ /// Number of logical CPU cores on the host.
+ [JsonPropertyName("cpu_count")]
+ public long? CpuCount { get; set; }
+
+ /// Distinct CPU model names for the host, comma-separated.
+ [JsonPropertyName("cpu_model")]
+ public string? CpuModel { get; set; }
+
/// Stable machine identifier for the device.
[JsonPropertyName("dev_device_id")]
public string? DevDeviceId { get; set; }
@@ -18301,6 +18727,74 @@ public sealed class GitHubTelemetryNotification
public string? SessionId { get; set; }
}
+/// SDK host response to a GitHub credential request.
+/// Polymorphic base type discriminated by kind.
+[Experimental(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 token variant of .
+[Experimental(Diagnostics.Experimental)]
+public partial class GitHubTokenAcquireResultToken : GitHubTokenAcquireResult
+{
+ ///
+ [JsonIgnore]
+ public override string Kind => "token";
+
+ /// GitHub access token acquired by the SDK host.
+ [JsonPropertyName("accessToken")]
+ public required string AccessToken { get; set; }
+
+ /// Remaining token lifetime in seconds when callback execution completes. It must exceed the one-hour preflight refresh threshold.
+ [JsonPropertyName("expiresIn")]
+ public required long ExpiresIn { get; set; }
+
+ /// OAuth token type. Defaults to bearer when omitted.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("tokenType")]
+ public string? TokenType { get; set; }
+}
+
+/// The cancelled variant of .
+[Experimental(Diagnostics.Experimental)]
+public partial class GitHubTokenAcquireResultCancelled : GitHubTokenAcquireResult
+{
+ ///
+ [JsonIgnore]
+ public override string Kind => "cancelled";
+}
+
+/// Asks the SDK client to acquire a GitHub access token from an opaque callback registration.
+[Experimental(Diagnostics.Experimental)]
+public sealed class GitHubTokenAcquireRequest
+{
+ /// Effective GitHub host for which the callback must return a token.
+ [JsonPropertyName("host")]
+ public string Host { get; set; } = string.Empty;
+
+ /// Why the runtime is requesting a GitHub credential.
+ [JsonPropertyName("reason")]
+ public GitHubTokenAcquireReason Reason { get; set; }
+
+ /// Opaque identifier generated by the SDK for this callback registration.
+ [JsonPropertyName("registrationId")]
+ public string RegistrationId { get; set; } = string.Empty;
+
+ /// Session receiving the token. Absent only before a cloud session has been assigned its id.
+ [JsonPropertyName("sessionId")]
+ public string? SessionId { get; set; }
+}
+
/// Resolved Anthropic adaptive-thinking capability for a model.
[Experimental(Diagnostics.Experimental)]
[JsonConverter(typeof(Converter))]
@@ -22563,6 +23057,9 @@ public AuthInfoType(string value)
/// 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");
@@ -25168,6 +25665,84 @@ public override void Write(Utf8JsonWriter writer, OptionsUpdateReasoningSummary
}
+/// Origin of the sandbox choice supplied by an internal client.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct SandboxConfigSource : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public SandboxConfigSource(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// The client applied the default because no sandbox preference was configured.
+ public static SandboxConfigSource NeverConfigured { get; } = new("never_configured");
+
+ /// The user's persisted settings enabled the sandbox.
+ public static SandboxConfigSource UserEnabled { get; } = new("user_enabled");
+
+ /// The user's persisted settings disabled the sandbox.
+ public static SandboxConfigSource UserDisabled { get; } = new("user_disabled");
+
+ /// A command-line flag selected the sandbox state for this session.
+ public static SandboxConfigSource SessionFlag { get; } = new("session_flag");
+
+ /// The user disabled the sandbox for the current session.
+ public static SandboxConfigSource SessionDisabled { get; } = new("session_disabled");
+
+ /// The client disabled the sandbox because the host cannot enforce it.
+ public static SandboxConfigSource UnsupportedHost { get; } = new("unsupported_host");
+
+ /// A repository policy selected the sandbox state.
+ public static SandboxConfigSource RepositoryPolicy { get; } = new("repository_policy");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(SandboxConfigSource left, SandboxConfigSource right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(SandboxConfigSource left, SandboxConfigSource right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is SandboxConfigSource other && Equals(other);
+
+ ///
+ public bool Equals(SandboxConfigSource 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 SandboxConfigSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, SandboxConfigSource value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SandboxConfigSource));
+ }
+ }
+}
+
+
/// Session capability enabled for this session.
[Experimental(Diagnostics.Experimental)]
[JsonConverter(typeof(Converter))]
@@ -26680,6 +27255,72 @@ public override void Write(Utf8JsonWriter writer, PermissionDecisionOutcome valu
}
+/// Response capability available to the client when it settled a permission request.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct PermissionResponseCapability : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public PermissionResponseCapability(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// The client could ask a user for this decision.
+ public static PermissionResponseCapability Interactive { get; } = new("interactive");
+
+ /// The client could return an automated response but could not ask a user.
+ public static PermissionResponseCapability Headless { get; } = new("headless");
+
+ /// The client had no response path available.
+ public static PermissionResponseCapability None { get; } = new("none");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(PermissionResponseCapability left, PermissionResponseCapability right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(PermissionResponseCapability left, PermissionResponseCapability right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is PermissionResponseCapability other && Equals(other);
+
+ ///
+ public bool Equals(PermissionResponseCapability 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 PermissionResponseCapability Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, PermissionResponseCapability value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(PermissionResponseCapability));
+ }
+ }
+}
+
+
/// Controlled reason or actor responsible for a permission response.
[Experimental(Diagnostics.Experimental)]
[JsonConverter(typeof(Converter))]
@@ -26778,6 +27419,9 @@ public PermissionDecisionSurface(string value)
/// The Copilot App client.
public static PermissionDecisionSurface CopilotApp { get; } = new("copilot_app");
+ /// An Agent Client Protocol host.
+ public static PermissionDecisionSurface Acp { get; } = new("acp");
+
/// A generic Copilot SDK client.
public static PermissionDecisionSurface Sdk { get; } = new("sdk");
@@ -28824,6 +29468,69 @@ public override void Write(Utf8JsonWriter writer, LlmInferenceHttpRequestStartTr
}
+/// Why the runtime is requesting a GitHub credential.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct GitHubTokenAcquireReason : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public GitHubTokenAcquireReason(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// The runtime is acquiring the registration's first credential.
+ public static GitHubTokenAcquireReason Initial { get; } = new("initial");
+
+ /// The runtime is replacing a credential that is approaching expiry.
+ public static GitHubTokenAcquireReason Refresh { get; } = new("refresh");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(GitHubTokenAcquireReason left, GitHubTokenAcquireReason right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(GitHubTokenAcquireReason left, GitHubTokenAcquireReason right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is GitHubTokenAcquireReason other && Equals(other);
+
+ ///
+ public bool Equals(GitHubTokenAcquireReason 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 GitHubTokenAcquireReason Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, GitHubTokenAcquireReason value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(GitHubTokenAcquireReason));
+ }
+ }
+}
+
+
/// Provides server-scoped RPC methods (no session required).
public sealed class ServerRpc
{
@@ -28847,13 +29554,14 @@ public async Task PingAsync(string? message = null, CancellationToke
/// Performs the SDK server connection handshake and validates the optional connection token. Marked internal because this is JSON-RPC transport plumbing invoked automatically by an SDK client's own `connect()` wrapper, not a user-facing method. Stays internal as long as the SDK client owns the handshake; would only become public if the SDK ever exposed the raw schema surface to consumers without a connection wrapper.
/// Opt this connection in to GitHub telemetry forwarding for its lifetime. When set, the runtime forwards every internal telemetry event it emits — across all sessions, plus sessionless events — to this connection over the `gitHubTelemetry.event` notification. Regular events are also written to the runtime's normal GitHub/CTS path (dual-write); host-only compatibility events are forward-only and intentionally skip that path. Intended for first-party hosts that re-emit the events into their own telemetry stores. Both unrestricted and restricted events are forwarded, each tagged with a `restricted` discriminator; a backstop drops restricted events when restricted telemetry is disabled — using the process-global gate for ordinary events and an explicit session-scoped decision for host-only events.
+ /// Identity of the integrating host. Optional; omit it to keep the default attribution.
/// Connection token; required when the server was started with COPILOT_CONNECTION_TOKEN.
/// The to monitor for cancellation requests. The default is .
/// Handshake result reporting the server's protocol version and package version on success.
[Experimental(Diagnostics.Experimental)]
- internal async Task ConnectAsync(bool? enableGitHubTelemetryForwarding = null, string? token = null, CancellationToken cancellationToken = default)
+ internal async Task ConnectAsync(bool? enableGitHubTelemetryForwarding = null, ConnectClientInfo? clientInfo = null, string? token = null, CancellationToken cancellationToken = default)
{
- var request = new ConnectRequest { EnableGitHubTelemetryForwarding = enableGitHubTelemetryForwarding, Token = token };
+ var request = new ConnectRequest { EnableGitHubTelemetryForwarding = enableGitHubTelemetryForwarding, ClientInfo = clientInfo, Token = token };
return await CopilotClient.InvokeRpcAsync(_rpc, "connect", [request], cancellationToken);
}
@@ -30267,6 +30975,12 @@ internal SessionRpc(CopilotSession session)
internal CopilotSession Session => _session;
+ /// Sandbox APIs.
+ public SandboxApi Sandbox =>
+ field ??
+ Interlocked.CompareExchange(ref field, new(_session), null) ??
+ field;
+
/// GitHubAuth APIs.
public GitHubAuthApi GitHubAuth =>
field ??
@@ -30639,6 +31353,29 @@ public async Task LogAsync(string message, SessionLogLevel? level = n
}
}
+/// Provides session-scoped Sandbox APIs.
+[Experimental(Diagnostics.Experimental)]
+public sealed class SandboxApi
+{
+ private readonly CopilotSession _session;
+
+ internal SandboxApi(CopilotSession session)
+ {
+ _session = session;
+ }
+
+ /// Returns whether managed policy requires sandbox enforcement and whether an enforcement failure has permanently blocked the session.
+ /// The to monitor for cancellation requests. The default is .
+ /// Managed sandbox enforcement state for a session.
+ public async Task GetEnforcementStatusAsync(CancellationToken cancellationToken = default)
+ {
+ _session.ThrowIfDisposed();
+
+ var request = new SessionSandboxGetEnforcementStatusRequest { SessionId = _session.SessionId };
+ return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.sandbox.getEnforcementStatus", [request], cancellationToken);
+ }
+}
+
/// Provides session-scoped GitHubAuth APIs.
[Experimental(Diagnostics.Experimental)]
public sealed class GitHubAuthApi
@@ -30665,7 +31402,7 @@ public async Task GetStatusAsync(CancellationToken cancellati
/// 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.
/// The to monitor for cancellation requests. The default is .
/// Indicates whether the credential update succeeded.
- public async Task SetCredentialsAsync(AuthInfo? credentials = null, CancellationToken cancellationToken = default)
+ public async Task SetCredentialsAsync(SettableAuthInfo? credentials = null, CancellationToken cancellationToken = default)
{
_session.ThrowIfDisposed();
@@ -30973,17 +31710,51 @@ public async Task RunAsync(string name, object args, RunOption
/// Resumes a factory run using its persisted name, arguments, journal, and accounting.
/// Factory run identifier.
/// Optional per-invocation resource ceiling overrides.
+ /// Whether to notify the originating session when the factory completes.
+ /// Whether to emit factory phase names to the session transcript.
/// The to monitor for cancellation requests. The default is .
/// Resolved persisted factory identity and resumed run envelope.
- public async Task ResumeAsync(string runId, FactoryRunLimits? limits = null, CancellationToken cancellationToken = default)
+ public async Task ResumeAsync(string runId, FactoryRunLimits? limits = null, bool? notifyOnComplete = null, bool? logPhaseNames = null, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(runId);
_session.ThrowIfDisposed();
- var request = new FactoryResumeRequest { SessionId = _session.SessionId, RunId = runId, Limits = limits };
+ var request = new FactoryResumeRequest { SessionId = _session.SessionId, RunId = runId, Limits = limits, NotifyOnComplete = notifyOnComplete, LogPhaseNames = logPhaseNames };
return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.factory.resume", [request], cancellationToken);
}
+ /// Internal tool-originated factory invocation.
+ /// Registered factory name.
+ /// Factory input value.
+ /// Tool-originated factory invocation options.
+ /// Opaque identifier of the originating tool call.
+ /// The to monitor for cancellation requests. The default is .
+ /// Complete current or terminal factory run envelope.
+ internal async Task RunFromToolAsync(string name, object args, FactoryToolRunOptions? options = null, string? toolCallId = null, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(name);
+ ArgumentNullException.ThrowIfNull(args);
+ _session.ThrowIfDisposed();
+
+ var request = new FactoryToolRunRequest { SessionId = _session.SessionId, Name = name, Args = CopilotClient.ToJsonElementForWire(args)!.Value, Options = options, ToolCallId = toolCallId };
+ return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.factory.runFromTool", [request], cancellationToken);
+ }
+
+ /// Internal tool-originated factory resume.
+ /// Factory run identifier.
+ /// Optional per-invocation resource ceiling overrides.
+ /// Opaque identifier of the originating tool call.
+ /// The to monitor for cancellation requests. The default is .
+ /// Resolved persisted factory identity and resumed run envelope.
+ internal async Task ResumeFromToolAsync(string runId, FactoryRunLimits? limits = null, string? toolCallId = null, CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(runId);
+ _session.ThrowIfDisposed();
+
+ var request = new FactoryToolResumeRequest { SessionId = _session.SessionId, RunId = runId, Limits = limits, ToolCallId = toolCallId };
+ return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.factory.resumeFromTool", [request], cancellationToken);
+ }
+
/// Gets the current or settled envelope for a factory run.
/// Factory run identifier.
/// The to monitor for cancellation requests. The default is .
@@ -32631,10 +33402,12 @@ internal OptionsApi(CopilotSession session)
/// Use shell.initProfile instead. Shell init profile (`None` or `NonInteractive`).
/// PowerShell process flags applied to built-in and user-requested shell commands.
/// Resolved sandbox configuration.
+ /// Origin of the sandbox choice. The runtime uses this only for internal telemetry provenance; managed policy is derived independently.
/// Whether interactive shell sessions are logged.
/// How env values are passed to MCP servers (`direct` inlines literal values; `indirect` resolves at launch).
/// Whether to include instructions from every MCP server in the system prompt instead of only allowlisted servers.
/// Additional directories to search for skills.
+ /// Built-in skill names to include in this session. When specified, only these runtime-bundled skills are available. Skills from other sources with the same name remain available. Set to null to remove the allowlist restriction.
/// Skill IDs that should be excluded from this session.
/// Whether to discover custom instructions on demand after successful file views (AGENTS.md / CLAUDE.md / .github/copilot-instructions.md surfacing). Combined with `skipCustomInstructions`.
/// 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.
@@ -32667,11 +33440,11 @@ internal OptionsApi(CopilotSession session)
/// Optional session limits. Pass null to clear the session limits.
/// The to monitor for cancellation requests. The default is .
/// Indicates whether the session options patch was applied successfully.
- public async Task UpdateAsync(string? model = null, ModelCapabilitiesOverride? modelCapabilitiesOverrides = null, string? reasoningEffort = null, OptionsUpdateReasoningSummary? reasoningSummary = null, Verbosity? verbosity = null, string? clientName = null, string? lspClientName = null, string? integrationId = null, IDictionary? featureFlags = null, bool? isExperimentalMode = null, ProviderConfig? provider = null, CapiSessionOptions? capi = null, string? workingDirectory = null, IList? availableTools = null, IList? excludedTools = null, IList? includedBuiltinAgents = null, IList? excludedBuiltinAgents = null, OptionsUpdateToolFilterPrecedence? toolFilterPrecedence = null, bool? enableScriptSafety = null, ShellOptions? shell = null, string? shellInitProfile = null, IList? shellProcessFlags = null, SandboxConfig? sandboxConfig = null, bool? logInteractiveShells = null, OptionsUpdateEnvValueMode? envValueMode = null, bool? allowAllMcpServerInstructions = null, IList? skillDirectories = null, IList? disabledSkills = null, bool? enableOnDemandInstructionDiscovery = null, long? maxInlineBinaryBytes = null, IList? installedPlugins = null, bool? customAgentsLocalOnly = null, bool? suppressCustomAgentPrompt = null, bool? skipCustomInstructions = null, IList? disabledInstructionSources = null, bool? coauthorEnabled = null, string? trajectoryFile = null, bool? enableStreaming = null, string? copilotUrl = null, bool? askUserDisabled = null, bool? continueOnAutoMode = null, bool? runningInInteractiveMode = null, bool? enableReasoningSummaries = null, string? agentContext = null, string? eventsLogDirectory = null, bool? eventsLogIncludesSubagents = null, IList? additionalContentExclusionPolicies = null, bool? manageScheduleEnabled = null, IList? sessionCapabilities = null, bool? skipEmbeddingRetrieval = null, string? organizationCustomInstructions = null, bool? enableFileHooks = null, bool? enableHostGitOperations = null, bool? enableSessionStore = null, bool? enableSkills = null, OptionsUpdateContextTier? contextTier = null, SessionLimitsConfig? sessionLimits = null, CancellationToken cancellationToken = default)
+ public async Task UpdateAsync(string? model = null, ModelCapabilitiesOverride? modelCapabilitiesOverrides = null, string? reasoningEffort = null, OptionsUpdateReasoningSummary? reasoningSummary = null, Verbosity? verbosity = null, string? clientName = null, string? lspClientName = null, string? integrationId = null, IDictionary? featureFlags = null, bool? isExperimentalMode = null, ProviderConfig? provider = null, CapiSessionOptions? capi = null, string? workingDirectory = null, IList? availableTools = null, IList? excludedTools = null, IList? includedBuiltinAgents = null, IList? excludedBuiltinAgents = null, OptionsUpdateToolFilterPrecedence? toolFilterPrecedence = null, bool? enableScriptSafety = null, ShellOptions? shell = null, string? shellInitProfile = null, IList? shellProcessFlags = null, SandboxConfig? sandboxConfig = null, SandboxConfigSource? sandboxConfigSource = null, bool? logInteractiveShells = null, OptionsUpdateEnvValueMode? envValueMode = null, bool? allowAllMcpServerInstructions = null, IList? skillDirectories = null, IList? includedBuiltinSkills = null, IList? disabledSkills = null, bool? enableOnDemandInstructionDiscovery = null, long? maxInlineBinaryBytes = null, IList? installedPlugins = null, bool? customAgentsLocalOnly = null, bool? suppressCustomAgentPrompt = null, bool? skipCustomInstructions = null, IList? disabledInstructionSources = null, bool? coauthorEnabled = null, string? trajectoryFile = null, bool? enableStreaming = null, string? copilotUrl = null, bool? askUserDisabled = null, bool? continueOnAutoMode = null, bool? runningInInteractiveMode = null, bool? enableReasoningSummaries = null, string? agentContext = null, string? eventsLogDirectory = null, bool? eventsLogIncludesSubagents = null, IList? additionalContentExclusionPolicies = null, bool? manageScheduleEnabled = null, IList? sessionCapabilities = null, bool? skipEmbeddingRetrieval = null, string? organizationCustomInstructions = null, bool? enableFileHooks = null, bool? enableHostGitOperations = null, bool? enableSessionStore = null, bool? enableSkills = null, OptionsUpdateContextTier? contextTier = null, SessionLimitsConfig? sessionLimits = null, CancellationToken cancellationToken = default)
{
_session.ThrowIfDisposed();
- var request = new SessionUpdateOptionsParams { SessionId = _session.SessionId, Model = model, ModelCapabilitiesOverrides = modelCapabilitiesOverrides, ReasoningEffort = reasoningEffort, ReasoningSummary = reasoningSummary, Verbosity = verbosity, ClientName = clientName, LspClientName = lspClientName, IntegrationId = integrationId, FeatureFlags = featureFlags, IsExperimentalMode = isExperimentalMode, Provider = provider, Capi = capi, WorkingDirectory = workingDirectory, AvailableTools = availableTools, ExcludedTools = excludedTools, IncludedBuiltinAgents = includedBuiltinAgents, ExcludedBuiltinAgents = excludedBuiltinAgents, ToolFilterPrecedence = toolFilterPrecedence, EnableScriptSafety = enableScriptSafety, Shell = shell, ShellInitProfile = shellInitProfile, ShellProcessFlags = shellProcessFlags, SandboxConfig = sandboxConfig, LogInteractiveShells = logInteractiveShells, EnvValueMode = envValueMode, AllowAllMcpServerInstructions = allowAllMcpServerInstructions, SkillDirectories = skillDirectories, DisabledSkills = disabledSkills, EnableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery, MaxInlineBinaryBytes = maxInlineBinaryBytes, InstalledPlugins = installedPlugins, CustomAgentsLocalOnly = customAgentsLocalOnly, SuppressCustomAgentPrompt = suppressCustomAgentPrompt, SkipCustomInstructions = skipCustomInstructions, DisabledInstructionSources = disabledInstructionSources, CoauthorEnabled = coauthorEnabled, TrajectoryFile = trajectoryFile, EnableStreaming = enableStreaming, CopilotUrl = copilotUrl, AskUserDisabled = askUserDisabled, ContinueOnAutoMode = continueOnAutoMode, RunningInInteractiveMode = runningInInteractiveMode, EnableReasoningSummaries = enableReasoningSummaries, AgentContext = agentContext, EventsLogDirectory = eventsLogDirectory, EventsLogIncludesSubagents = eventsLogIncludesSubagents, AdditionalContentExclusionPolicies = additionalContentExclusionPolicies, ManageScheduleEnabled = manageScheduleEnabled, SessionCapabilities = sessionCapabilities, SkipEmbeddingRetrieval = skipEmbeddingRetrieval, OrganizationCustomInstructions = organizationCustomInstructions, EnableFileHooks = enableFileHooks, EnableHostGitOperations = enableHostGitOperations, EnableSessionStore = enableSessionStore, EnableSkills = enableSkills, ContextTier = contextTier, SessionLimits = sessionLimits };
+ var request = new SessionUpdateOptionsParams { SessionId = _session.SessionId, Model = model, ModelCapabilitiesOverrides = modelCapabilitiesOverrides, ReasoningEffort = reasoningEffort, ReasoningSummary = reasoningSummary, Verbosity = verbosity, ClientName = clientName, LspClientName = lspClientName, IntegrationId = integrationId, FeatureFlags = featureFlags, IsExperimentalMode = isExperimentalMode, Provider = provider, Capi = capi, WorkingDirectory = workingDirectory, AvailableTools = availableTools, ExcludedTools = excludedTools, IncludedBuiltinAgents = includedBuiltinAgents, ExcludedBuiltinAgents = excludedBuiltinAgents, ToolFilterPrecedence = toolFilterPrecedence, EnableScriptSafety = enableScriptSafety, Shell = shell, ShellInitProfile = shellInitProfile, ShellProcessFlags = shellProcessFlags, SandboxConfig = sandboxConfig, SandboxConfigSource = sandboxConfigSource, LogInteractiveShells = logInteractiveShells, EnvValueMode = envValueMode, AllowAllMcpServerInstructions = allowAllMcpServerInstructions, SkillDirectories = skillDirectories, IncludedBuiltinSkills = includedBuiltinSkills, DisabledSkills = disabledSkills, EnableOnDemandInstructionDiscovery = enableOnDemandInstructionDiscovery, MaxInlineBinaryBytes = maxInlineBinaryBytes, InstalledPlugins = installedPlugins, CustomAgentsLocalOnly = customAgentsLocalOnly, SuppressCustomAgentPrompt = suppressCustomAgentPrompt, SkipCustomInstructions = skipCustomInstructions, DisabledInstructionSources = disabledInstructionSources, CoauthorEnabled = coauthorEnabled, TrajectoryFile = trajectoryFile, EnableStreaming = enableStreaming, CopilotUrl = copilotUrl, AskUserDisabled = askUserDisabled, ContinueOnAutoMode = continueOnAutoMode, RunningInInteractiveMode = runningInInteractiveMode, EnableReasoningSummaries = enableReasoningSummaries, AgentContext = agentContext, EventsLogDirectory = eventsLogDirectory, EventsLogIncludesSubagents = eventsLogIncludesSubagents, AdditionalContentExclusionPolicies = additionalContentExclusionPolicies, ManageScheduleEnabled = manageScheduleEnabled, SessionCapabilities = sessionCapabilities, SkipEmbeddingRetrieval = skipEmbeddingRetrieval, OrganizationCustomInstructions = organizationCustomInstructions, EnableFileHooks = enableFileHooks, EnableHostGitOperations = enableHostGitOperations, EnableSessionStore = enableSessionStore, EnableSkills = enableSkills, ContextTier = contextTier, SessionLimits = sessionLimits };
return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.options.update", [request], cancellationToken);
}
}
@@ -32799,22 +33572,20 @@ public async Task ExecuteAsync(string name, object arguments, strin
}
/// Returns the Rust-owned built-in tool descriptors used to construct the session's offered tool set.
- /// Whether line numbers should be omitted from the view tool descriptor.
/// Whether descriptors should favor fewer user-intervention prompts.
/// Whether tool descriptors should include authoring metadata.
/// Whether semantic skill lookup is available.
/// Shell-specific names and description lines for shell tools.
- /// Whether shell commands may only run asynchronously.
/// Whether the configured shell supports PowerShell 7 syntax.
/// Default shell timeout in milliseconds.
/// Whether background task completion notifications are enabled.
/// The to monitor for cancellation requests. The default is .
/// Rust-owned built-in tool descriptors for the session.
- public async Task GetBuiltinDescriptorsAsync(bool? noViewLineNumbers = null, bool? reduceUserIntervention = null, bool? includeAuthor = null, bool? skillEmbeddingEnabled = null, ToolsShellDescriptorConfig? shellConfig = null, bool? shellAsyncOnlyEnabled = null, bool? shellSupportsPowerShell7Syntax = null, double? shellTimeoutMs = null, bool? backgroundTaskNotificationsEnabled = null, CancellationToken cancellationToken = default)
+ public async Task GetBuiltinDescriptorsAsync(bool? reduceUserIntervention = null, bool? includeAuthor = null, bool? skillEmbeddingEnabled = null, ToolsShellDescriptorConfig? shellConfig = null, bool? shellSupportsPowerShell7Syntax = null, double? shellTimeoutMs = null, bool? backgroundTaskNotificationsEnabled = null, CancellationToken cancellationToken = default)
{
_session.ThrowIfDisposed();
- var request = new ToolsGetBuiltinDescriptorsRequest { SessionId = _session.SessionId, NoViewLineNumbers = noViewLineNumbers, ReduceUserIntervention = reduceUserIntervention, IncludeAuthor = includeAuthor, SkillEmbeddingEnabled = skillEmbeddingEnabled, ShellConfig = shellConfig, ShellAsyncOnlyEnabled = shellAsyncOnlyEnabled, ShellSupportsPowerShell7Syntax = shellSupportsPowerShell7Syntax, ShellTimeoutMs = shellTimeoutMs, BackgroundTaskNotificationsEnabled = backgroundTaskNotificationsEnabled };
+ var request = new ToolsGetBuiltinDescriptorsRequest { SessionId = _session.SessionId, ReduceUserIntervention = reduceUserIntervention, IncludeAuthor = includeAuthor, SkillEmbeddingEnabled = skillEmbeddingEnabled, ShellConfig = shellConfig, ShellSupportsPowerShell7Syntax = shellSupportsPowerShell7Syntax, ShellTimeoutMs = shellTimeoutMs, BackgroundTaskNotificationsEnabled = backgroundTaskNotificationsEnabled };
return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.tools.getBuiltinDescriptors", [request], cancellationToken);
}
@@ -32979,14 +33750,15 @@ public async Task ExecuteAsync(string commandName, string
/// Enqueues a slash command for FIFO processing on the local session.
/// 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.
+ /// Optional user-facing text for the queue row. The command string is shown when omitted.
/// The to monitor for cancellation requests. The default is .
/// Indicates whether the command was accepted into the local execution queue.
- public async Task EnqueueAsync(string command, CancellationToken cancellationToken = default)
+ public async Task EnqueueAsync(string command, string? displayText = null, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(command);
_session.ThrowIfDisposed();
- var request = new EnqueueCommandParams { SessionId = _session.SessionId, Command = command };
+ var request = new EnqueueCommandParams { SessionId = _session.SessionId, Command = command, DisplayText = displayText };
return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.commands.enqueue", [request], cancellationToken);
}
@@ -33388,8 +34160,8 @@ public async Task ListAsync(CancellationToken cancellationT
return await CopilotClient.InvokeRpcAsync(_session.Rpc, "session.permissions.paths.list", [request], cancellationToken);
}
- /// Adds a directory to the session's allow-list.
- /// Directory to add to the allow-list. The runtime resolves and validates the path before adding.
+ /// Adds a directory to the session's allow-list and activates conventional skill and agent definitions under it.
+ /// 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.
/// The to monitor for cancellation requests. The default is .
/// Indicates whether the operation succeeded.
public async Task AddAsync(string path, CancellationToken cancellationToken = default)
@@ -34786,6 +35558,17 @@ public interface IGitHubTelemetryHandler
Task EventAsync(GitHubTelemetryNotification request, CancellationToken cancellationToken = default);
}
+/// Handles `gitHubToken` client global API methods.
+[Experimental(Diagnostics.Experimental)]
+public interface IGitHubTokenHandler
+{
+ /// Asks the SDK client to mint a GitHub access token for a session whose configuration supplied a GitHub token provider. The runtime acquires the initial token during bootstrap and refreshes it during expiry preflight when one hour or less remains.
+ /// Asks the SDK client to acquire a GitHub access token from an opaque callback registration.
+ /// The to monitor for cancellation requests. The default is .
+ /// SDK host response to a GitHub credential request.
+ Task GetTokenAsync(GitHubTokenAcquireRequest request, CancellationToken cancellationToken = default);
+}
+
/// Provides all client global API handler groups for a connection.
public sealed class ClientGlobalApiHandlers
{
@@ -34797,6 +35580,9 @@ public sealed class ClientGlobalApiHandlers
/// Optional handler for GitHubTelemetry client global API methods.
public IGitHubTelemetryHandler? GitHubTelemetry { get; set; }
+
+ /// Optional handler for GitHubToken client global API methods.
+ public IGitHubTokenHandler? GitHubToken { get; set; }
}
/// Registers client global API handlers on a JSON-RPC connection.
@@ -34830,6 +35616,11 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
var handler = handlers.GitHubTelemetry ?? throw new InvalidOperationException("No gitHubTelemetry client-global handler registered");
await handler.EventAsync(request, cancellationToken);
}), singleObjectParam: true);
+ rpc.SetLocalRpcMethod("gitHubToken.getToken", (Func>)(async (request, cancellationToken) =>
+ {
+ var handler = handlers.GitHubToken ?? throw new InvalidOperationException("No gitHubToken client-global handler registered");
+ return await handler.GetTokenAsync(request, cancellationToken);
+ }), singleObjectParam: true);
}
}
@@ -34849,6 +35640,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(GitHub.Copilot.AgentInterruptedCancelPhase), TypeInfoPropertyName = "SessionEventsAgentInterruptedCancelPhase")]
[JsonSerializable(typeof(GitHub.Copilot.AgentInterruptedData), TypeInfoPropertyName = "SessionEventsAgentInterruptedData")]
[JsonSerializable(typeof(GitHub.Copilot.AgentInterruptedEvent), TypeInfoPropertyName = "SessionEventsAgentInterruptedEvent")]
+[JsonSerializable(typeof(GitHub.Copilot.AssistantFusionPhaseCompletedEvent), TypeInfoPropertyName = "SessionEventsAssistantFusionPhaseCompletedEvent")]
+[JsonSerializable(typeof(GitHub.Copilot.AssistantFusionPhaseFailedEvent), TypeInfoPropertyName = "SessionEventsAssistantFusionPhaseFailedEvent")]
+[JsonSerializable(typeof(GitHub.Copilot.AssistantFusionPhaseStartedEvent), TypeInfoPropertyName = "SessionEventsAssistantFusionPhaseStartedEvent")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantIdleData), TypeInfoPropertyName = "SessionEventsAssistantIdleData")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantIdleEvent), TypeInfoPropertyName = "SessionEventsAssistantIdleEvent")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantIntentData), TypeInfoPropertyName = "SessionEventsAssistantIntentData")]
@@ -34857,10 +35651,13 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageDeltaData), TypeInfoPropertyName = "SessionEventsAssistantMessageDeltaData")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageDeltaEvent), TypeInfoPropertyName = "SessionEventsAssistantMessageDeltaEvent")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageEvent), TypeInfoPropertyName = "SessionEventsAssistantMessageEvent")]
+[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageReasoningBlocks), TypeInfoPropertyName = "SessionEventsAssistantMessageReasoningBlocks")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageServerTools), TypeInfoPropertyName = "SessionEventsAssistantMessageServerTools")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageStartData), TypeInfoPropertyName = "SessionEventsAssistantMessageStartData")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageStartEvent), TypeInfoPropertyName = "SessionEventsAssistantMessageStartEvent")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageToolRequest), TypeInfoPropertyName = "SessionEventsAssistantMessageToolRequest")]
+[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageToolRequestCaller), TypeInfoPropertyName = "SessionEventsAssistantMessageToolRequestCaller")]
+[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageToolRequestCallerType), TypeInfoPropertyName = "SessionEventsAssistantMessageToolRequestCallerType")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantMessageToolRequestType), TypeInfoPropertyName = "SessionEventsAssistantMessageToolRequestType")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantReasoningData), TypeInfoPropertyName = "SessionEventsAssistantReasoningData")]
[JsonSerializable(typeof(GitHub.Copilot.AssistantReasoningDeltaData), TypeInfoPropertyName = "SessionEventsAssistantReasoningDeltaData")]
@@ -34915,6 +35712,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(GitHub.Copilot.AutoModeSwitchRequestedData), TypeInfoPropertyName = "SessionEventsAutoModeSwitchRequestedData")]
[JsonSerializable(typeof(GitHub.Copilot.AutoModeSwitchRequestedEvent), TypeInfoPropertyName = "SessionEventsAutoModeSwitchRequestedEvent")]
[JsonSerializable(typeof(GitHub.Copilot.AutoModeSwitchResponse), TypeInfoPropertyName = "SessionEventsAutoModeSwitchResponse")]
+[JsonSerializable(typeof(GitHub.Copilot.AutoTier), TypeInfoPropertyName = "SessionEventsAutoTier")]
[JsonSerializable(typeof(GitHub.Copilot.AutopilotObjectiveChangedOperation), TypeInfoPropertyName = "SessionEventsAutopilotObjectiveChangedOperation")]
[JsonSerializable(typeof(GitHub.Copilot.AutopilotObjectiveChangedStatus), TypeInfoPropertyName = "SessionEventsAutopilotObjectiveChangedStatus")]
[JsonSerializable(typeof(GitHub.Copilot.BinaryAssetReference), TypeInfoPropertyName = "SessionEventsBinaryAssetReference")]
@@ -34979,6 +35777,17 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(GitHub.Copilot.FactoryRunStartedEvent), TypeInfoPropertyName = "SessionEventsFactoryRunStartedEvent")]
[JsonSerializable(typeof(GitHub.Copilot.FactoryRunUpdatedData), TypeInfoPropertyName = "SessionEventsFactoryRunUpdatedData")]
[JsonSerializable(typeof(GitHub.Copilot.FactoryRunUpdatedEvent), TypeInfoPropertyName = "SessionEventsFactoryRunUpdatedEvent")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionAttribution), TypeInfoPropertyName = "SessionEventsFusionAttribution")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionConversationScope), TypeInfoPropertyName = "SessionEventsFusionConversationScope")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionFollowUpAction), TypeInfoPropertyName = "SessionEventsFusionFollowUpAction")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionFollowUpRecommendation), TypeInfoPropertyName = "SessionEventsFusionFollowUpRecommendation")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionPattern), TypeInfoPropertyName = "SessionEventsFusionPattern")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionPhaseKind), TypeInfoPropertyName = "SessionEventsFusionPhaseKind")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionPhaseStatus), TypeInfoPropertyName = "SessionEventsFusionPhaseStatus")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionPhaseUsage), TypeInfoPropertyName = "SessionEventsFusionPhaseUsage")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionProjectionMode), TypeInfoPropertyName = "SessionEventsFusionProjectionMode")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionScores), TypeInfoPropertyName = "SessionEventsFusionScores")]
+[JsonSerializable(typeof(GitHub.Copilot.FusionTurnKind), TypeInfoPropertyName = "SessionEventsFusionTurnKind")]
[JsonSerializable(typeof(GitHub.Copilot.GitHubMcpToolConfig), TypeInfoPropertyName = "SessionEventsGitHubMcpToolConfig")]
[JsonSerializable(typeof(GitHub.Copilot.GitHubRepoRef), TypeInfoPropertyName = "SessionEventsGitHubRepoRef")]
[JsonSerializable(typeof(GitHub.Copilot.HandoffRepository), TypeInfoPropertyName = "SessionEventsHandoffRepository")]
@@ -35028,6 +35837,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(GitHub.Copilot.ModelCallFailureRequestFingerprint), TypeInfoPropertyName = "SessionEventsModelCallFailureRequestFingerprint")]
[JsonSerializable(typeof(GitHub.Copilot.ModelCallFailureSource), TypeInfoPropertyName = "SessionEventsModelCallFailureSource")]
[JsonSerializable(typeof(GitHub.Copilot.ModelCallFailureTransport), TypeInfoPropertyName = "SessionEventsModelCallFailureTransport")]
+[JsonSerializable(typeof(GitHub.Copilot.ModelCallFinishedData), TypeInfoPropertyName = "SessionEventsModelCallFinishedData")]
+[JsonSerializable(typeof(GitHub.Copilot.ModelCallFinishedEvent), TypeInfoPropertyName = "SessionEventsModelCallFinishedEvent")]
+[JsonSerializable(typeof(GitHub.Copilot.ModelCallFinishedOutcome), TypeInfoPropertyName = "SessionEventsModelCallFinishedOutcome")]
[JsonSerializable(typeof(GitHub.Copilot.ModelCallStartData), TypeInfoPropertyName = "SessionEventsModelCallStartData")]
[JsonSerializable(typeof(GitHub.Copilot.ModelCallStartEvent), TypeInfoPropertyName = "SessionEventsModelCallStartEvent")]
[JsonSerializable(typeof(GitHub.Copilot.ModelChangeSource), TypeInfoPropertyName = "SessionEventsModelChangeSource")]
@@ -35117,6 +35929,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(GitHub.Copilot.SkillsLoadedSkill), TypeInfoPropertyName = "SessionEventsSkillsLoadedSkill")]
[JsonSerializable(typeof(GitHub.Copilot.SubagentCompletedData), TypeInfoPropertyName = "SessionEventsSubagentCompletedData")]
[JsonSerializable(typeof(GitHub.Copilot.SubagentCompletedEvent), TypeInfoPropertyName = "SessionEventsSubagentCompletedEvent")]
+[JsonSerializable(typeof(GitHub.Copilot.SubagentConfiguredData), TypeInfoPropertyName = "SessionEventsSubagentConfiguredData")]
+[JsonSerializable(typeof(GitHub.Copilot.SubagentConfiguredEvent), TypeInfoPropertyName = "SessionEventsSubagentConfiguredEvent")]
[JsonSerializable(typeof(GitHub.Copilot.SubagentDeselectedData), TypeInfoPropertyName = "SessionEventsSubagentDeselectedData")]
[JsonSerializable(typeof(GitHub.Copilot.SubagentDeselectedEvent), TypeInfoPropertyName = "SessionEventsSubagentDeselectedEvent")]
[JsonSerializable(typeof(GitHub.Copilot.SubagentFailedData), TypeInfoPropertyName = "SessionEventsSubagentFailedData")]
@@ -35283,6 +36097,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(CompletionsRequestRequest))]
[JsonSerializable(typeof(CompletionsRequestResult))]
[JsonSerializable(typeof(ConfigureSessionExtensionsParams))]
+[JsonSerializable(typeof(ConnectClientInfo))]
[JsonSerializable(typeof(ConnectRemoteSessionParams))]
[JsonSerializable(typeof(ConnectRequest))]
[JsonSerializable(typeof(ConnectResult))]
@@ -35366,6 +36181,9 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(FactoryRunResult))]
[JsonSerializable(typeof(FactoryRunSummary))]
[JsonSerializable(typeof(FactoryRunTerminal))]
+[JsonSerializable(typeof(FactoryToolResumeRequest))]
+[JsonSerializable(typeof(FactoryToolRunOptions))]
+[JsonSerializable(typeof(FactoryToolRunRequest))]
[JsonSerializable(typeof(FleetStartRequest))]
[JsonSerializable(typeof(FleetStartResult))]
[JsonSerializable(typeof(FolderTrustAddParams))]
@@ -35374,6 +36192,8 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(GitHubTelemetryClientInfo))]
[JsonSerializable(typeof(GitHubTelemetryEvent))]
[JsonSerializable(typeof(GitHubTelemetryNotification))]
+[JsonSerializable(typeof(GitHubTokenAcquireRequest))]
+[JsonSerializable(typeof(GitHubTokenAcquireResult))]
[JsonSerializable(typeof(HandlePendingToolCallRequest))]
[JsonSerializable(typeof(HandlePendingToolCallResult))]
[JsonSerializable(typeof(HistoryAbortManualCompactionResult))]
@@ -35560,6 +36380,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(ModelCapabilitiesOverrideSupports))]
[JsonSerializable(typeof(ModelCapabilitiesSupports))]
[JsonSerializable(typeof(ModelList))]
+[JsonSerializable(typeof(ModelMessage))]
[JsonSerializable(typeof(ModelPickerPersistenceRequest))]
[JsonSerializable(typeof(ModelPickerSettingsContext))]
[JsonSerializable(typeof(ModelPickerSettingsContextEnvironment))]
@@ -35569,6 +36390,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(ModelSwitchConfirmation))]
[JsonSerializable(typeof(ModelSwitchToRequest))]
[JsonSerializable(typeof(ModelSwitchToResult))]
+[JsonSerializable(typeof(ModelWarningText))]
[JsonSerializable(typeof(ModelsListRequest))]
[JsonSerializable(typeof(MoveMcpLoadingToBackgroundResult))]
[JsonSerializable(typeof(NameGetResult))]
@@ -35729,6 +36551,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(SandboxConfigUserPolicyNetwork))]
[JsonSerializable(typeof(SandboxConfigUserPolicyNetworkProxy))]
[JsonSerializable(typeof(SandboxConfigUserPolicySeatbelt))]
+[JsonSerializable(typeof(SandboxEnforcementStatus))]
[JsonSerializable(typeof(ScheduleAddAtRequest))]
[JsonSerializable(typeof(ScheduleAddCronRequest))]
[JsonSerializable(typeof(ScheduleAddRequest))]
@@ -35865,6 +36688,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(SessionQueueRemoveMostRecentRequest))]
[JsonSerializable(typeof(SessionQueueSnapshotRequest))]
[JsonSerializable(typeof(SessionRemoteDisableRequest))]
+[JsonSerializable(typeof(SessionSandboxGetEnforcementStatusRequest))]
[JsonSerializable(typeof(SessionScheduleHasSelfPacedRequest))]
[JsonSerializable(typeof(SessionScheduleHydrateRequest))]
[JsonSerializable(typeof(SessionScheduleListRequest))]
@@ -35949,6 +36773,7 @@ public static void RegisterClientGlobalApiHandlers(JsonRpc rpc, ClientGlobalApiH
[JsonSerializable(typeof(SessionsStartRemoteControlRequest))]
[JsonSerializable(typeof(SessionsStopRemoteControlRequest))]
[JsonSerializable(typeof(SessionsTransferRemoteControlRequest))]
+[JsonSerializable(typeof(SettableAuthInfo))]
[JsonSerializable(typeof(ShellCancelUserRequestedRequest))]
[JsonSerializable(typeof(ShellCredentials))]
[JsonSerializable(typeof(ShellExecRequest))]
diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs
index fa0563a8e4..4c7ebc6ffa 100644
--- a/dotnet/src/Generated/SessionEvents.cs
+++ b/dotnet/src/Generated/SessionEvents.cs
@@ -26,6 +26,9 @@ namespace GitHub.Copilot;
IgnoreUnrecognizedTypeDiscriminators = true)]
[JsonDerivedType(typeof(AbortEvent), "abort")]
[JsonDerivedType(typeof(AgentInterruptedEvent), "agent.interrupted")]
+[JsonDerivedType(typeof(AssistantFusionPhaseCompletedEvent), "assistant.fusion_phase_completed")]
+[JsonDerivedType(typeof(AssistantFusionPhaseFailedEvent), "assistant.fusion_phase_failed")]
+[JsonDerivedType(typeof(AssistantFusionPhaseStartedEvent), "assistant.fusion_phase_started")]
[JsonDerivedType(typeof(AssistantIdleEvent), "assistant.idle")]
[JsonDerivedType(typeof(AssistantIntentEvent), "assistant.intent")]
[JsonDerivedType(typeof(AssistantMessageEvent), "assistant.message")]
@@ -68,6 +71,7 @@ namespace GitHub.Copilot;
[JsonDerivedType(typeof(McpResourcesListChangedEvent), "mcp.resources.list_changed")]
[JsonDerivedType(typeof(McpToolsListChangedEvent), "mcp.tools.list_changed")]
[JsonDerivedType(typeof(ModelCallFailureEvent), "model.call_failure")]
+[JsonDerivedType(typeof(ModelCallFinishedEvent), "model.call_finished")]
[JsonDerivedType(typeof(ModelCallStartEvent), "model.call_start")]
[JsonDerivedType(typeof(PendingMessagesModifiedEvent), "pending_messages.modified")]
[JsonDerivedType(typeof(PermissionCompletedEvent), "permission.completed")]
@@ -97,6 +101,10 @@ namespace GitHub.Copilot;
[JsonDerivedType(typeof(SessionErrorEvent), "session.error")]
[JsonDerivedType(typeof(SessionExtensionsLoadedEvent), "session.extensions_loaded")]
[JsonDerivedType(typeof(SessionExtensionsAttachmentsPushedEvent), "session.extensions.attachments_pushed")]
+[JsonDerivedType(typeof(SessionFusionCompletedEvent), "session.fusion_completed")]
+[JsonDerivedType(typeof(SessionFusionResolvedEvent), "session.fusion_resolved")]
+[JsonDerivedType(typeof(SessionFusionRouteFailedEvent), "session.fusion_route_failed")]
+[JsonDerivedType(typeof(SessionFusionRouteStartedEvent), "session.fusion_route_started")]
[JsonDerivedType(typeof(SessionHandoffEvent), "session.handoff")]
[JsonDerivedType(typeof(SessionIdleEvent), "session.idle")]
[JsonDerivedType(typeof(SessionInfoEvent), "session.info")]
@@ -129,6 +137,7 @@ namespace GitHub.Copilot;
[JsonDerivedType(typeof(SessionWorkspaceFileChangedEvent), "session.workspace_file_changed")]
[JsonDerivedType(typeof(SkillInvokedEvent), "skill.invoked")]
[JsonDerivedType(typeof(SubagentCompletedEvent), "subagent.completed")]
+[JsonDerivedType(typeof(SubagentConfiguredEvent), "subagent.configured")]
[JsonDerivedType(typeof(SubagentDeselectedEvent), "subagent.deselected")]
[JsonDerivedType(typeof(SubagentFailedEvent), "subagent.failed")]
[JsonDerivedType(typeof(SubagentSelectedEvent), "subagent.selected")]
@@ -578,6 +587,62 @@ public sealed partial class SessionTaskCompleteEvent : SessionEvent
public required SessionTaskCompleteData Data { get; set; }
}
+/// Experimental transient signal that HydraFusion routing has started for an eligible turn.
+/// Represents the session.fusion_route_started event.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class SessionFusionRouteStartedEvent : SessionEvent
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "session.fusion_route_started";
+
+ /// The session.fusion_route_started event payload.
+ [JsonPropertyName("data")]
+ public required SessionFusionRouteStartedData Data { get; set; }
+}
+
+/// Experimental durable HydraFusion routing failure and the deterministic concrete fallback selected for the turn.
+/// Represents the session.fusion_route_failed event.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class SessionFusionRouteFailedEvent : SessionEvent
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "session.fusion_route_failed";
+
+ /// The session.fusion_route_failed event payload.
+ [JsonPropertyName("data")]
+ public required SessionFusionRouteFailedData Data { get; set; }
+}
+
+/// Experimental durable validated HydraFusion route and turn policy.
+/// Represents the session.fusion_resolved event.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class SessionFusionResolvedEvent : SessionEvent
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "session.fusion_resolved";
+
+ /// The session.fusion_resolved event payload.
+ [JsonPropertyName("data")]
+ public required SessionFusionResolvedData Data { get; set; }
+}
+
+/// Experimental durable aggregate outcome of a HydraFusion turn.
+/// Represents the session.fusion_completed event.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class SessionFusionCompletedEvent : SessionEvent
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "session.fusion_completed";
+
+ /// The session.fusion_completed event payload.
+ [JsonPropertyName("data")]
+ public required SessionFusionCompletedData Data { get; set; }
+}
+
/// Payload of `user.message` with displayed and model-transformed content, attachments, source/delivery metadata, mode, and telemetry IDs.
/// Represents the user.message event.
public sealed partial class UserMessageEvent : SessionEvent
@@ -656,6 +721,48 @@ public sealed partial class AssistantIntentEvent : SessionEvent
public required AssistantIntentData Data { get; set; }
}
+/// Experimental transient HydraFusion phase/model/role signal.
+/// Represents the assistant.fusion_phase_started event.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class AssistantFusionPhaseStartedEvent : SessionEvent
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "assistant.fusion_phase_started";
+
+ /// The assistant.fusion_phase_started event payload.
+ [JsonPropertyName("data")]
+ public required AssistantFusionPhaseStartedData Data { get; set; }
+}
+
+/// Experimental durable HydraFusion phase output and lossless replay checkpoint.
+/// Represents the assistant.fusion_phase_completed event.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class AssistantFusionPhaseCompletedEvent : SessionEvent
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "assistant.fusion_phase_completed";
+
+ /// The assistant.fusion_phase_completed event payload.
+ [JsonPropertyName("data")]
+ public required AssistantFusionPhaseCompletedData Data { get; set; }
+}
+
+/// Experimental durable typed HydraFusion phase failure and degradation transition.
+/// Represents the assistant.fusion_phase_failed event.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class AssistantFusionPhaseFailedEvent : SessionEvent
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "assistant.fusion_phase_failed";
+
+ /// The assistant.fusion_phase_failed event payload.
+ [JsonPropertyName("data")]
+ public required AssistantFusionPhaseFailedData Data { get; set; }
+}
+
/// Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message.
/// Represents the assistant.server_tool_progress event.
public sealed partial class AssistantServerToolProgressEvent : SessionEvent
@@ -825,6 +932,19 @@ public sealed partial class ModelCallFailureEvent : SessionEvent
public required ModelCallFailureData Data { get; set; }
}
+/// Final lifecycle outcome for one logical model dispatch. A logical dispatch may include internal reconnect or fallback work, so event count is not provider HTTP-request count.
+/// Represents the model.call_finished event.
+public sealed partial class ModelCallFinishedEvent : SessionEvent
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "model.call_finished";
+
+ /// The model.call_finished event payload.
+ [JsonPropertyName("data")]
+ public required ModelCallFinishedData Data { get; set; }
+}
+
/// Model API dispatch metadata for internal telemetry.
/// Represents the model.call_start event.
public sealed partial class ModelCallStartEvent : SessionEvent
@@ -968,6 +1088,19 @@ public sealed partial class SubagentStartedEvent : SessionEvent
public required SubagentStartedData Data { get; set; }
}
+/// Resolved runtime configuration for a configured sub-agent.
+/// Represents the subagent.configured event.
+public sealed partial class SubagentConfiguredEvent : SessionEvent
+{
+ ///
+ [JsonIgnore]
+ public override string Type => "subagent.configured";
+
+ /// The subagent.configured event payload.
+ [JsonPropertyName("data")]
+ public required SubagentConfiguredData Data { get; set; }
+}
+
/// Sub-agent completion details for successful execution.
/// Represents the subagent.completed event.
public sealed partial class SubagentCompletedEvent : SessionEvent
@@ -1783,6 +1916,11 @@ public sealed partial class SessionStartData
[JsonPropertyName("alreadyInUse")]
public bool? AlreadyInUse { get; set; }
+ /// Auto routing preference selected at session creation time.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("autoTier")]
+ public AutoTier? AutoTier { get; set; }
+
/// Working directory and git context at session start.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("context")]
@@ -1862,6 +2000,11 @@ public sealed partial class SessionResumeData
[JsonPropertyName("alreadyInUse")]
public bool? AlreadyInUse { get; set; }
+ /// Auto routing preference active at resume time.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("autoTier")]
+ public AutoTier? AutoTier { get; set; }
+
/// Updated working directory and git context at resume time.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("context")]
@@ -1988,6 +2131,11 @@ public sealed partial class SessionIdleData
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("aborted")]
public bool? Aborted { get; set; }
+
+ /// The session mode the agent was operating in when it went idle, when the mode is known. Lets turn-scoped consumers distinguish an autopilot continuation boundary (where the agent keeps working after this idle) from a genuine turn completion.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("mode")]
+ public SessionMode? Mode { get; set; }
}
/// Session title change payload containing the new display title.
@@ -2584,6 +2732,11 @@ public sealed partial class SessionCompactionStartData
/// Conversation compaction results including success status, metrics, and optional error details.
public sealed partial class SessionCompactionCompleteData
{
+ /// Canonical model identifier used for model-specific behavior when replaying compaction.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("behaviorModelId")]
+ public string? BehaviorModelId { get; set; }
+
/// Checkpoint snapshot number created for recovery.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("checkpointNumber")]
@@ -2713,6 +2866,237 @@ public sealed partial class SessionTaskCompleteData
public string? Summary { get; set; }
}
+/// Experimental transient signal that HydraFusion routing has started for an eligible turn.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class SessionFusionRouteStartedData
+{
+ /// Identifier for this routing attempt before a durable Fusion turn exists.
+ [JsonPropertyName("attemptId")]
+ public required string AttemptId { get; set; }
+
+ /// HydraFusion routing policy requested for the turn.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("policy")]
+ public string? Policy { get; set; }
+
+ /// Synthetic HydraFusion model selected for the session.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("syntheticModel")]
+ public string? SyntheticModel { get; set; }
+
+ /// Kind of turn being routed.
+ [JsonPropertyName("turnKind")]
+ public required FusionTurnKind TurnKind { get; set; }
+}
+
+/// Experimental durable HydraFusion routing failure and the deterministic concrete fallback selected for the turn.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class SessionFusionRouteFailedData
+{
+ /// Identifier of the routing attempt that failed.
+ [JsonPropertyName("attemptId")]
+ public required string AttemptId { get; set; }
+
+ /// Provider or validation error detail, when available.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("errorMessage")]
+ public string? ErrorMessage { get; set; }
+
+ /// Concrete model selected as the deterministic fallback.
+ [JsonPropertyName("fallbackModel")]
+ public required string FallbackModel { get; set; }
+
+ /// HydraFusion routing policy requested for the turn.
+ [JsonPropertyName("policy")]
+ public required string Policy { get; set; }
+
+ /// Stable machine-readable reason for the routing failure.
+ [JsonPropertyName("reason")]
+ public required string Reason { get; set; }
+
+ /// Elapsed routing time in milliseconds before the failure.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("routingLatencyMs")]
+ public double? RoutingLatencyMs { get; set; }
+
+ /// Synthetic HydraFusion model selected for the session.
+ [JsonPropertyName("syntheticModel")]
+ public required string SyntheticModel { get; set; }
+}
+
+/// Experimental durable validated HydraFusion route and turn policy.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class SessionFusionResolvedData
+{
+ /// Version of the validated HydraFusion event contract.
+ [JsonPropertyName("contractVersion")]
+ public required long ContractVersion { get; set; }
+
+ /// Concrete model used when the planned primary model cannot execute.
+ [JsonPropertyName("fallbackModel")]
+ public required string FallbackModel { get; set; }
+
+ /// Router recommendation controlling reuse or rerouting on later turns.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("followUp")]
+ public FusionFollowUpRecommendation? FollowUp { get; set; }
+
+ /// Concrete model recommended for eligible follow-up turns.
+ [JsonPropertyName("followUpModel")]
+ public required string FollowUpModel { get; set; }
+
+ /// Stable identifier for the resolved HydraFusion turn.
+ [JsonPropertyName("fusionId")]
+ public required string FusionId { get; set; }
+
+ /// Version of the executable model universe used for selection.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("modelUniverseVersion")]
+ public string? ModelUniverseVersion { get; set; }
+
+ /// Validated orchestration pattern selected for the turn.
+ [JsonPropertyName("pattern")]
+ public required FusionPattern Pattern { get; set; }
+
+ /// Version of the validated execution-plan format.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("planVersion")]
+ public string? PlanVersion { get; set; }
+
+ /// HydraFusion routing policy used to resolve the plan.
+ [JsonPropertyName("policy")]
+ public required string Policy { get; set; }
+
+ /// Version of the local routing policy.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("policyVersion")]
+ public string? PolicyVersion { get; set; }
+
+ /// Concrete model selected for the primary solver phase.
+ [JsonPropertyName("primaryModel")]
+ public required string PrimaryModel { get; set; }
+
+ /// Router implementation that supplied the plan.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("routeSource")]
+ public string? RouteSource { get; set; }
+
+ /// Elapsed time in milliseconds required to resolve and validate the route.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("routingLatencyMs")]
+ public double? RoutingLatencyMs { get; set; }
+
+ /// Identifier of the local policy rule that matched.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("ruleId")]
+ public string? RuleId { get; set; }
+
+ /// Zero-based index of the local policy rule that matched.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("ruleIndex")]
+ public long? RuleIndex { get; set; }
+
+ /// Human-readable name of the local policy rule that matched.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("ruleName")]
+ public string? RuleName { get; set; }
+
+ /// Validated capability scores used to select the route.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("scores")]
+ public FusionScores? Scores { get; set; }
+
+ /// Concrete model selected for the review or judge phase, when required.
+ [JsonPropertyName("secondaryModel")]
+ public string? SecondaryModel { get; set; }
+
+ /// Synthetic HydraFusion model selected for the session.
+ [JsonPropertyName("syntheticModel")]
+ public required string SyntheticModel { get; set; }
+
+ /// Identifier of the session turn associated with the route.
+ [JsonPropertyName("turnId")]
+ public required string TurnId { get; set; }
+}
+
+/// Experimental durable aggregate outcome of a HydraFusion turn.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class SessionFusionCompletedData
+{
+ /// Total cached input tokens reported across all phases.
+ [JsonPropertyName("cachedTokens")]
+ public required long CachedTokens { get; set; }
+
+ /// Total tokens written to prompt cache across all phases.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("cacheWriteTokens")]
+ public long? CacheWriteTokens { get; set; }
+
+ /// Idempotency identifier for the authoritative final commit.
+ [JsonPropertyName("commitId")]
+ public required string CommitId { get; set; }
+
+ /// Reason the turn used a degraded route, when applicable.
+ [JsonPropertyName("degradedReason")]
+ public string? DegradedReason { get; set; }
+
+ /// Total elapsed execution time for the HydraFusion turn in milliseconds.
+ [JsonPropertyName("durationMs")]
+ public required double DurationMs { get; set; }
+
+ /// Concrete model that supplied the authoritative final content.
+ [JsonPropertyName("finalSourceModel")]
+ public string? FinalSourceModel { get; set; }
+
+ /// Phase whose output supplied the authoritative final content.
+ [JsonPropertyName("finalSourcePhaseId")]
+ public string? FinalSourcePhaseId { get; set; }
+
+ /// Concrete model recommended for eligible follow-up turns.
+ [JsonPropertyName("followUpModel")]
+ public required string FollowUpModel { get; set; }
+
+ /// Stable identifier for the completed HydraFusion turn.
+ [JsonPropertyName("fusionId")]
+ public required string FusionId { get; set; }
+
+ /// Total input tokens consumed across all phases.
+ [JsonPropertyName("inputTokens")]
+ public required long InputTokens { get; set; }
+
+ /// Stable aggregate outcome of the HydraFusion turn.
+ [JsonPropertyName("outcome")]
+ public required string Outcome { get; set; }
+
+ /// Total output tokens produced across all phases.
+ [JsonPropertyName("outputTokens")]
+ public required long OutputTokens { get; set; }
+
+ /// HydraFusion orchestration pattern executed for the turn.
+ [JsonPropertyName("pattern")]
+ public required FusionPattern Pattern { get; set; }
+
+ /// Number of concrete phases attempted by the turn.
+ [JsonPropertyName("phaseCount")]
+ public required long PhaseCount { get; set; }
+
+ /// Total concrete model requests made across all phases.
+ [JsonPropertyName("requestCount")]
+ public required long RequestCount { get; set; }
+
+ /// Synthetic HydraFusion model selected for the session.
+ [JsonPropertyName("syntheticModel")]
+ public required string SyntheticModel { get; set; }
+
+ /// Total normalized AI-unit cost reported across all phases, in nano-AIU.
+ [JsonPropertyName("totalNanoAiu")]
+ public required double TotalNanoAiu { get; set; }
+
+ /// Identifier of the session turn associated with the completion.
+ [JsonPropertyName("turnId")]
+ public required string TurnId { get; set; }
+}
+
/// Payload of `user.message` with displayed and model-transformed content, attachments, source/delivery metadata, mode, and telemetry IDs.
public sealed partial class UserMessageData
{
@@ -2893,6 +3277,161 @@ public sealed partial class AssistantIntentData
public required string Intent { get; set; }
}
+/// Experimental transient HydraFusion phase/model/role signal.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class AssistantFusionPhaseStartedData
+{
+ /// Conversation scope in which the phase executes.
+ [JsonPropertyName("conversationScope")]
+ public required FusionConversationScope ConversationScope { get; set; }
+
+ /// Identifier of the HydraFusion turn containing the phase.
+ [JsonPropertyName("fusionId")]
+ public required string FusionId { get; set; }
+
+ /// Concrete model executing the phase.
+ [JsonPropertyName("model")]
+ public required string Model { get; set; }
+
+ /// HydraFusion orchestration pattern containing the phase.
+ [JsonPropertyName("pattern")]
+ public required FusionPattern Pattern { get; set; }
+
+ /// Stable identifier for the concrete phase.
+ [JsonPropertyName("phaseId")]
+ public required string PhaseId { get; set; }
+
+ /// Kind of phase being executed.
+ [JsonPropertyName("phaseKind")]
+ public required FusionPhaseKind PhaseKind { get; set; }
+
+ /// Semantic role assigned to the phase.
+ [JsonPropertyName("role")]
+ public required string Role { get; set; }
+}
+
+/// Experimental durable HydraFusion phase output and lossless replay checkpoint.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class AssistantFusionPhaseCompletedData
+{
+ /// Provider-normalized textual output produced by the phase.
+ [JsonPropertyName("content")]
+ public required string Content { get; set; }
+
+ /// Conversation scope in which the phase executed.
+ [JsonPropertyName("conversationScope")]
+ public required FusionConversationScope ConversationScope { get; set; }
+
+ /// Elapsed execution time for the phase in milliseconds.
+ [JsonPropertyName("durationMs")]
+ public required double DurationMs { get; set; }
+
+ /// Identifier of the HydraFusion turn containing the phase.
+ [JsonPropertyName("fusionId")]
+ public required string FusionId { get; set; }
+
+ /// Concrete model that executed the phase.
+ [JsonPropertyName("model")]
+ public required string Model { get; set; }
+
+ /// Stable identifier for the completed phase.
+ [JsonPropertyName("phaseId")]
+ public required string PhaseId { get; set; }
+
+ /// Kind of phase that completed.
+ [JsonPropertyName("phaseKind")]
+ public required FusionPhaseKind PhaseKind { get; set; }
+
+ /// Exact provider-normalized message used to reconstruct canonical model history.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonInclude]
+ [JsonPropertyName("projectionMessage")]
+ internal JsonElement? ProjectionMessage { get; set; }
+
+ /// Projection action for the exact internal message.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonInclude]
+ [JsonPropertyName("projectionMode")]
+ internal FusionProjectionMode? ProjectionMode { get; set; }
+
+ /// Semantic role assigned to the completed phase.
+ [JsonPropertyName("role")]
+ public required string Role { get; set; }
+
+ /// Terminal request held outside canonical state until selected by the final commit.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonInclude]
+ [JsonPropertyName("stagedTerminal")]
+ internal FusionStagedTerminal? StagedTerminal { get; set; }
+
+ /// Durable outcome status of the phase.
+ [JsonPropertyName("status")]
+ public required FusionPhaseStatus Status { get; set; }
+
+ /// Aggregate concrete-model usage consumed by the phase.
+ [JsonPropertyName("usage")]
+ public required FusionPhaseUsage Usage { get; set; }
+
+ /// Structured judge or critic verdict, when the phase produces one.
+ [JsonPropertyName("verdict")]
+ public string? Verdict { get; set; }
+}
+
+/// Experimental durable typed HydraFusion phase failure and degradation transition.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class AssistantFusionPhaseFailedData
+{
+ /// Conversation scope in which the phase executed.
+ [JsonPropertyName("conversationScope")]
+ public required FusionConversationScope ConversationScope { get; set; }
+
+ /// Identifier of the fallback phase used to continue the turn after degradation.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("degradedToPhaseId")]
+ public string? DegradedToPhaseId { get; set; }
+
+ /// Elapsed execution time before the phase failed, in milliseconds.
+ [JsonPropertyName("durationMs")]
+ public required double DurationMs { get; set; }
+
+ /// Provider or execution error detail, when available.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("errorMessage")]
+ public string? ErrorMessage { get; set; }
+
+ /// Identifier of the HydraFusion turn containing the phase.
+ [JsonPropertyName("fusionId")]
+ public required string FusionId { get; set; }
+
+ /// Concrete model that attempted the phase.
+ [JsonPropertyName("model")]
+ public required string Model { get; set; }
+
+ /// Stable identifier for the failed phase.
+ [JsonPropertyName("phaseId")]
+ public required string PhaseId { get; set; }
+
+ /// Kind of phase that failed.
+ [JsonPropertyName("phaseKind")]
+ public required FusionPhaseKind PhaseKind { get; set; }
+
+ /// Stable machine-readable reason for the phase failure.
+ [JsonPropertyName("reason")]
+ public required string Reason { get; set; }
+
+ /// Semantic role assigned to the failed phase.
+ [JsonPropertyName("role")]
+ public required string Role { get; set; }
+
+ /// Durable outcome status of the phase.
+ [JsonPropertyName("status")]
+ public required FusionPhaseStatus Status { get; set; }
+
+ /// Aggregate concrete-model usage consumed before the failure.
+ [JsonPropertyName("usage")]
+ public required FusionPhaseUsage Usage { get; set; }
+}
+
/// Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message.
public sealed partial class AssistantServerToolProgressData
{
@@ -3006,6 +3545,12 @@ public sealed partial class AssistantMessageData
[JsonPropertyName("encryptedContent")]
public string? EncryptedContent { get; set; }
+ /// Experimental HydraFusion source attribution for this ordinary authoritative assistant message.
+ [Experimental(Diagnostics.Experimental)]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("fusion")]
+ public FusionAttribution? Fusion { get; set; }
+
/// CAPI interaction ID for correlating this message with upstream telemetry.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("interactionId")]
@@ -3039,6 +3584,11 @@ public sealed partial class AssistantMessageData
[JsonPropertyName("phase")]
public string? Phase { 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")]
+ public AssistantMessageReasoningBlocks? ReasoningBlocks { get; set; }
+
/// Opaque/encrypted extended thinking data from Anthropic models. Session-bound and stripped on resume.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("reasoningOpaque")]
@@ -3225,6 +3775,12 @@ public sealed partial class AssistantUsageData
[JsonPropertyName("frontierSource")]
internal string? FrontierSource { get; set; }
+ /// Experimental HydraFusion attribution for this concrete model call's usage.
+ [Experimental(Diagnostics.Experimental)]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("fusion")]
+ public FusionAttribution? Fusion { get; set; }
+
/// What initiated this API call (e.g., "sub-agent", "mcp-sampling"); absent for user-initiated calls.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("initiator")]
@@ -3281,9 +3837,15 @@ public sealed partial class AssistantUsageData
[JsonPropertyName("outputTokens")]
public long? OutputTokens { get; set; }
- /// Parent tool call ID when this usage originates from a sub-agent.
- [EditorBrowsable(EditorBrowsableState.Never)]
-#if NET5_0_OR_GREATER
+ /// Time to first observable model output in milliseconds. Includes text, reasoning, and tool-call output; only available for streaming requests that produce observable output.
+ [JsonConverter(typeof(MillisecondsTimeSpanConverter))]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("outputTtftMs")]
+ public TimeSpan? OutputTtft { get; set; }
+
+ /// Parent tool call ID when this usage originates from a sub-agent.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+#if NET5_0_OR_GREATER
[Obsolete("This member is deprecated and will be removed in a future version.", DiagnosticId = "GHCP001")]
#endif
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
@@ -3529,6 +4091,12 @@ public sealed partial class ModelCallFailureData
[JsonPropertyName("failureKind")]
public ModelCallFailureKind? FailureKind { get; set; }
+ /// Experimental HydraFusion attribution for this failed concrete model call.
+ [Experimental(Diagnostics.Experimental)]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("fusion")]
+ public FusionAttribution? Fusion { get; set; }
+
/// What initiated this API call (e.g., "sub-agent", "mcp-sampling"); absent for user-initiated calls.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("initiator")]
@@ -3610,9 +4178,46 @@ public sealed partial class ModelCallFailureData
public ModelCallFailureTransport? Transport { get; set; }
}
+/// Final lifecycle outcome for one logical model dispatch. A logical dispatch may include internal reconnect or fallback work, so event count is not provider HTTP-request count.
+public sealed partial class ModelCallFinishedData
+{
+ /// Whether an accepted successful response requested the exact name and command semantics of a built-in file edit tool, including an external tool explicitly replacing that built-in name. Absent when the logical dispatch did not produce an accepted response.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("containsBuiltInFileEditRequest")]
+ public bool? ContainsBuiltInFileEditRequest { get; set; }
+
+ /// Monotonic elapsed time spent in the logical model dispatch, including any internal transport reconnect or fallback and excluding orchestrator retry backoff, tool execution, confirmations, and post-response processing.
+ [JsonConverter(typeof(MillisecondsTimeSpanConverter))]
+ [JsonPropertyName("dispatchDurationMs")]
+ public required TimeSpan DispatchDuration { get; set; }
+
+ /// Version of the built-in file-edit semantic classifier used for this event.
+ [JsonPropertyName("editClassifierVersion")]
+ public required long EditClassifierVersion { get; set; }
+
+ /// Identifier of the user interaction that owns the model dispatch, matching assistant.turn_start.interactionId when available.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("interactionId")]
+ public string? InteractionId { get; set; }
+
+ /// Final outcome after post-response acceptance processing.
+ [JsonPropertyName("outcome")]
+ public required ModelCallFinishedOutcome Outcome { get; set; }
+
+ /// Agent-loop iteration within the interaction that initiated the model dispatch.
+ [JsonPropertyName("turnId")]
+ public required string TurnId { get; set; }
+}
+
/// Model API dispatch metadata for internal telemetry.
public sealed partial class ModelCallStartData
{
+ /// Experimental HydraFusion attribution for this concrete model call.
+ [Experimental(Diagnostics.Experimental)]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("fusion")]
+ public FusionAttribution? Fusion { get; set; }
+
/// Model identifier used for this API call, when known.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("model")]
@@ -3667,6 +4272,12 @@ public sealed partial class ToolExecutionStartData
[JsonPropertyName("displayVerbatim")]
public bool? DisplayVerbatim { get; set; }
+ /// Experimental HydraFusion attribution for this tool execution.
+ [Experimental(Diagnostics.Experimental)]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("fusion")]
+ public FusionAttribution? Fusion { get; set; }
+
/// Name of the MCP server hosting this tool, when the tool is an MCP tool.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("mcpServerName")]
@@ -3752,6 +4363,12 @@ public sealed partial class ToolExecutionCompleteData
[JsonPropertyName("error")]
public ToolExecutionCompleteError? Error { get; set; }
+ /// Experimental HydraFusion attribution for this tool completion.
+ [Experimental(Diagnostics.Experimental)]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("fusion")]
+ public FusionAttribution? Fusion { get; set; }
+
/// CAPI interaction ID for correlating this tool execution with upstream telemetry.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("interactionId")]
@@ -3902,6 +4519,16 @@ public sealed partial class SubagentStartedData
[JsonPropertyName("agentName")]
public required string AgentName { get; set; }
+ /// Type of the sub-agent selected at spawn time.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("agentType")]
+ public string? AgentType { get; set; }
+
+ /// Whether the sub-agent runs synchronously or in the background.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("executionMode")]
+ public string? ExecutionMode { get; set; }
+
/// Root id of the factory run that spawned this sub-agent, when it was spawned by one.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("factoryRunId")]
@@ -3912,11 +4539,43 @@ public sealed partial class SubagentStartedData
[JsonPropertyName("model")]
public string? Model { get; set; }
+ /// Task-registry ID of the spawning sub-agent. Absent when the root session spawned this child.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("parentId")]
+ public string? ParentId { get; set; }
+
+ /// Whether this sub-agent can be resumed. Currently always false.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("resumable")]
+ public bool? Resumable { get; set; }
+
/// Tool call ID of the parent tool invocation that spawned this sub-agent.
[JsonPropertyName("toolCallId")]
public required string ToolCallId { get; set; }
}
+/// Resolved runtime configuration for a configured sub-agent.
+public sealed partial class SubagentConfiguredData
+{
+ /// Resolved context tier, when configured for the model.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("contextTier")]
+ public string? ContextTier { get; set; }
+
+ /// Resolved model the sub-agent will run with.
+ [JsonPropertyName("model")]
+ public required string Model { get; set; }
+
+ /// Whether the sub-agent accepts follow-up turns.
+ [JsonPropertyName("multiTurn")]
+ public required bool MultiTurn { get; set; }
+
+ /// Resolved reasoning effort, when configured for the model.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("reasoningEffort")]
+ public string? ReasoningEffort { get; set; }
+}
+
/// Sub-agent completion details for successful execution.
public sealed partial class SubagentCompletedData
{
@@ -3933,12 +4592,37 @@ public sealed partial class SubagentCompletedData
[JsonPropertyName("cancelled")]
public bool? Cancelled { get; set; }
+ /// Whether the first model actually dispatched matched the user's configured preference.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("configuredModelMatchesActual")]
+ public bool? ConfiguredModelMatchesActual { get; set; }
+
+ /// Concrete model the user configured for this sub-agent via `/subagents`, when present.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("configuredModelPreference")]
+ public string? ConfiguredModelPreference { get; set; }
+
/// Wall-clock duration of the sub-agent execution in milliseconds.
[JsonConverter(typeof(MillisecondsTimeSpanConverter))]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("durationMs")]
public TimeSpan? Duration { get; set; }
+ /// Whether the explicit task-call model matched the user's configured preference.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("explicitModelMatchesPreference")]
+ public bool? ExplicitModelMatchesPreference { get; set; }
+
+ /// Explicit model supplied by the parent agent on the task call, when present.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("explicitModelOverride")]
+ public string? ExplicitModelOverride { get; set; }
+
+ /// First model for which the sub-agent started an inference request, when one was dispatched.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("firstDispatchedModel")]
+ public string? FirstDispatchedModel { get; set; }
+
/// Model used by the sub-agent.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("model")]
@@ -3970,6 +4654,16 @@ public sealed partial class SubagentFailedData
[JsonPropertyName("agentName")]
public required string AgentName { get; set; }
+ /// Whether the first model actually dispatched matched the user's configured preference.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("configuredModelMatchesActual")]
+ public bool? ConfiguredModelMatchesActual { get; set; }
+
+ /// Concrete model the user configured for this sub-agent via `/subagents`, when present.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("configuredModelPreference")]
+ public string? ConfiguredModelPreference { get; set; }
+
/// Wall-clock duration of the sub-agent execution in milliseconds.
[JsonConverter(typeof(MillisecondsTimeSpanConverter))]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
@@ -3980,6 +4674,21 @@ public sealed partial class SubagentFailedData
[JsonPropertyName("error")]
public required string Error { get; set; }
+ /// Whether the explicit task-call model matched the user's configured preference.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("explicitModelMatchesPreference")]
+ public bool? ExplicitModelMatchesPreference { get; set; }
+
+ /// Explicit model supplied by the parent agent on the task call, when present.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("explicitModelOverride")]
+ public string? ExplicitModelOverride { get; set; }
+
+ /// First model for which the sub-agent started an inference request, when one was dispatched.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("firstDispatchedModel")]
+ public string? FirstDispatchedModel { get; set; }
+
/// Model selected for the sub-agent, when known.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("model")]
@@ -4036,6 +4745,11 @@ public sealed partial class HookStartData
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("input")]
public JsonElement? Input { get; set; }
+
+ /// Tool call ID of the parent tool invocation when this event originates from a sub-agent.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("parentToolCallId")]
+ public string? ParentToolCallId { get; set; }
}
/// Hook invocation completion details including output, success status, and error information.
@@ -4059,6 +4773,11 @@ public sealed partial class HookEndData
[JsonPropertyName("output")]
public JsonElement? Output { get; set; }
+ /// Tool call ID of the parent tool invocation when this event originates from a sub-agent.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("parentToolCallId")]
+ public string? ParentToolCallId { get; set; }
+
/// Whether the hook completed successfully.
[JsonPropertyName("success")]
public required bool Success { get; set; }
@@ -4721,6 +5440,11 @@ public sealed partial class SessionManagedSettingsResolvedData
[JsonPropertyName("permissionsAllowIntersected")]
public bool? PermissionsAllowIntersected { get; set; }
+ /// Whether the effective sandbox policy forces the sandbox on *only* because managed policy could not be determined, rather than because the policy requires it. Lets clients tell a user whose `--no-sandbox` was overridden that the sandbox stayed on as a fail-closed fallback, instead of attributing it to an administrator who set no such policy.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("sandboxEnabledByUndeterminedPolicy")]
+ public bool? SandboxEnabledByUndeterminedPolicy { get; set; }
+
/// Whether the server (account/org) managed-settings layer was present.
[JsonPropertyName("serverManaged")]
public required bool ServerManaged { get; set; }
@@ -5482,6 +6206,42 @@ public sealed partial class CompactionCompleteCompactionTokensUsed
public long? OutputTokens { get; set; }
}
+/// Durable server recommendation for subsequent HydraFusion turns.
+/// Nested data type for FusionFollowUpRecommendation.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class FusionFollowUpRecommendation
+{
+ /// Recommended routing action for the next compaction turn.
+ [JsonPropertyName("compactionTurn")]
+ public required FusionFollowUpAction CompactionTurn { get; set; }
+
+ /// Recommended routing action for the next user-message turn.
+ [JsonPropertyName("userTurn")]
+ public required FusionFollowUpAction UserTurn { get; set; }
+}
+
+/// Validated HydraFusion routing capability scores.
+/// Nested data type for FusionScores.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class FusionScores
+{
+ /// Code-generation capability score returned by the authenticated router.
+ [JsonPropertyName("codeGen")]
+ public required double CodeGen { get; set; }
+
+ /// Debugging capability score returned by the authenticated router.
+ [JsonPropertyName("debugging")]
+ public required double Debugging { get; set; }
+
+ /// Reasoning capability score returned by the authenticated router.
+ [JsonPropertyName("reasoning")]
+ public required double Reasoning { get; set; }
+
+ /// Tool-use capability score returned by the authenticated router.
+ [JsonPropertyName("toolUse")]
+ public required double ToolUse { get; set; }
+}
+
/// Optional line range to scope the attachment to a specific section of the file.
/// Nested data type for AttachmentFileLineRange.
public sealed partial class AttachmentFileLineRange
@@ -6032,6 +6792,63 @@ public partial class Attachment
}
+/// Internal durable terminal request staged by a HydraFusion phase until an idempotent final commit selects it.
+/// Nested data type for FusionStagedTerminal.
+[Experimental(Diagnostics.Experimental)]
+internal sealed partial class FusionStagedTerminal
+{
+ /// Gets or sets the arguments value.
+ [JsonPropertyName("arguments")]
+ public required string Arguments { get; set; }
+
+ /// Gets or sets the assistantMessage value.
+ [JsonPropertyName("assistantMessage")]
+ public required JsonElement AssistantMessage { get; set; }
+
+ /// Gets or sets the phaseId value.
+ [JsonPropertyName("phaseId")]
+ public required string PhaseId { get; set; }
+
+ /// Gets or sets the toolCallId value.
+ [JsonPropertyName("toolCallId")]
+ public required string ToolCallId { get; set; }
+
+ /// Gets or sets the toolName value.
+ [JsonPropertyName("toolName")]
+ public required string ToolName { get; set; }
+}
+
+/// Aggregate concrete-model usage for one HydraFusion phase.
+/// Nested data type for FusionPhaseUsage.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class FusionPhaseUsage
+{
+ /// Total cached input tokens reported for the phase.
+ [JsonPropertyName("cachedTokens")]
+ public required long CachedTokens { get; set; }
+
+ /// Total tokens written to prompt cache during the phase.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("cacheWriteTokens")]
+ public long? CacheWriteTokens { get; set; }
+
+ /// Total input tokens consumed by the phase.
+ [JsonPropertyName("inputTokens")]
+ public required long InputTokens { get; set; }
+
+ /// Total output tokens produced by the phase.
+ [JsonPropertyName("outputTokens")]
+ public required long OutputTokens { get; set; }
+
+ /// Number of concrete model requests made by the phase.
+ [JsonPropertyName("requestCount")]
+ public required long RequestCount { get; set; }
+
+ /// Total normalized AI-unit cost reported for the phase, in nano-AIU.
+ [JsonPropertyName("totalNanoAiu")]
+ public required double TotalNanoAiu { get; set; }
+}
+
/// A source that backs one or more cited spans in the assistant's response.
/// Nested data type for CitationSource.
[Experimental(Diagnostics.Experimental)]
@@ -6189,6 +7006,78 @@ public sealed partial class Citations
public required CitationSpan[] Spans { get; set; }
}
+/// Experimental attribution linking an ordinary event to the HydraFusion turn, phase, and concrete source that produced it.
+/// Nested data type for FusionAttribution.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class FusionAttribution
+{
+ /// Idempotency identifier for the authoritative commit, when the event belongs to the selected output.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("commitId")]
+ public string? CommitId { get; set; }
+
+ /// Conversation scope in which the concrete phase executed.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("conversationScope")]
+ public string? ConversationScope { get; set; }
+
+ /// Stable identifier for the HydraFusion turn that produced the event.
+ [JsonPropertyName("fusionId")]
+ public required string FusionId { get; set; }
+
+ /// HydraFusion orchestration pattern selected for the turn.
+ [JsonPropertyName("pattern")]
+ public required string Pattern { get; set; }
+
+ /// Identifier of the concrete phase that produced the event.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("phaseId")]
+ public string? PhaseId { get; set; }
+
+ /// Kind of concrete phase that produced the event.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("phaseKind")]
+ public string? PhaseKind { get; set; }
+
+ /// HydraFusion routing policy used for the turn.
+ [JsonPropertyName("policy")]
+ public required string Policy { get; set; }
+
+ /// Semantic role assigned to the concrete phase.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("role")]
+ public string? Role { get; set; }
+
+ /// Concrete model that produced the attributed event.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("sourceModel")]
+ public string? SourceModel { get; set; }
+
+ /// Phase whose output supplied the authoritative content, when different from the executing phase.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("sourcePhaseId")]
+ public string? SourcePhaseId { get; set; }
+
+ /// Synthetic HydraFusion model selected for the session.
+ [JsonPropertyName("syntheticModel")]
+ public required string SyntheticModel { get; set; }
+}
+
+/// Neutral provider-tagged reasoning content blocks preserved verbatim for round-tripping.
+/// Nested data type for AssistantMessageReasoningBlocks.
+[Experimental(Diagnostics.Experimental)]
+public sealed partial class AssistantMessageReasoningBlocks
+{
+ /// Provider-native reasoning content blocks (e.g. Anthropic `thinking` / `redacted_thinking`) preserved verbatim, in order. A single response can carry several, each signed over the content preceding it, so dropping or reordering any of them invalidates the rest.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("blocks")]
+ public JsonElement[]? Blocks { get; set; }
+
+ /// Model provider that produced these reasoning blocks.
+ [JsonPropertyName("provider")]
+ public required string Provider { get; set; }
+}
+
/// Neutral provider-tagged server-side tool-use payload (tool search, advisor) for verbatim round-tripping.
/// Nested data type for AssistantMessageServerTools.
[Experimental(Diagnostics.Experimental)]
@@ -6219,6 +7108,19 @@ public sealed partial class AssistantMessageServerTools
public JsonElement[]? RawContentBlocks { get; set; }
}
+/// Hosted program that requested this client tool call.
+/// Nested data type for AssistantMessageToolRequestCaller.
+public sealed partial class AssistantMessageToolRequestCaller
+{
+ /// Provider-assigned identifier for the hosted caller.
+ [JsonPropertyName("callerId")]
+ public required string CallerId { get; set; }
+
+ /// Kind of hosted caller that requested the client tool call.
+ [JsonPropertyName("type")]
+ public required AssistantMessageToolRequestCallerType Type { get; set; }
+}
+
/// A tool invocation request from the assistant.
/// Nested data type for AssistantMessageToolRequest.
public sealed partial class AssistantMessageToolRequest
@@ -6228,6 +7130,11 @@ public sealed partial class AssistantMessageToolRequest
[JsonPropertyName("arguments")]
public JsonElement? Arguments { get; set; }
+ /// Hosted program that requested this client tool call.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("caller")]
+ public AssistantMessageToolRequestCaller? Caller { get; set; }
+
/// Resolved intention summary describing what this specific call does.
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[JsonPropertyName("intentionSummary")]
@@ -6752,6 +7659,11 @@ public sealed partial class ToolExecutionCompleteContentShellExit : ToolExecutio
[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")]
@@ -8288,6 +9200,11 @@ public sealed partial class PermissionPromptRequestMcp : PermissionPromptRequest
[JsonPropertyName("assistedApproval")]
public PermissionAssistedApproval? AssistedApproval { get; set; }
+ /// Whether the host may offer a server-wide "approve all tools from this server" blanket. Absent is treated as true; the runtime sends false when managed policy disables bypass-permissions mode, which forbids the server-wide escalation while still allowing per-tool approval.
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("canOfferServerWideApproval")]
+ public bool? CanOfferServerWideApproval { get; set; }
+
/// Advisory runtime permission recommendation. The host remains responsible for deciding the request and may reject it.
[Experimental(Diagnostics.Experimental)]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
@@ -9416,6 +10333,70 @@ public sealed partial class McpAppToolCallCompleteToolMeta
public McpAppToolCallCompleteToolMetaUI? Ui { get; set; }
}
+/// Routing preference used when the session model is `auto`.
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct AutoTier : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public AutoTier(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// Optimize for efficiency.
+ public static AutoTier Efficiency { get; } = new("efficiency");
+
+ /// Balance efficiency and intelligence.
+ public static AutoTier Balance { get; } = new("balance");
+
+ /// Optimize for intelligence.
+ public static AutoTier Intelligence { get; } = new("intelligence");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(AutoTier left, AutoTier right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(AutoTier left, AutoTier right) => !(left == right);
+
+ ///
+ 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 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 AutoTier Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, AutoTier value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AutoTier));
+ }
+ }
+}
+
/// Hosting platform type of the repository (github or ado).
[JsonConverter(typeof(Converter))]
[DebuggerDisplay("{Value,nq}")]
@@ -9666,6 +10647,70 @@ public override void Write(Utf8JsonWriter writer, Verbosity value, JsonSerialize
}
}
+/// The session mode the agent is operating in.
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct SessionMode : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public SessionMode(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// The agent is responding interactively to the user.
+ public static SessionMode Interactive { get; } = new("interactive");
+
+ /// The agent is preparing a plan before making changes.
+ public static SessionMode Plan { get; } = new("plan");
+
+ /// The agent is working autonomously toward task completion.
+ public static SessionMode Autopilot { get; } = new("autopilot");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(SessionMode left, SessionMode right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(SessionMode left, SessionMode right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is SessionMode other && Equals(other);
+
+ ///
+ public bool Equals(SessionMode 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 SessionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, SessionMode value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionMode));
+ }
+ }
+}
+
/// Who created the schedule: `user` (an explicit user action such as `/every` or `/after`) or `model` (the agent via the `manage_schedule` tool). Gates whether a scheduled skill that opted out of model invocation may fire: only user-created schedules may.
[JsonConverter(typeof(Converter))]
[DebuggerDisplay("{Value,nq}")]
@@ -9946,70 +10991,6 @@ public override void Write(Utf8JsonWriter writer, ModelChangeSource value, JsonS
}
}
-/// The session mode the agent is operating in.
-[JsonConverter(typeof(Converter))]
-[DebuggerDisplay("{Value,nq}")]
-public readonly struct SessionMode : IEquatable
-{
- private readonly string? _value;
-
- /// Initializes a new instance of the struct.
- /// The value to associate with this .
- [JsonConstructor]
- public SessionMode(string value)
- {
- ArgumentException.ThrowIfNullOrWhiteSpace(value);
- _value = value;
- }
-
- /// Gets the value associated with this .
- public string Value => _value ?? string.Empty;
-
- /// The agent is responding interactively to the user.
- public static SessionMode Interactive { get; } = new("interactive");
-
- /// The agent is preparing a plan before making changes.
- public static SessionMode Plan { get; } = new("plan");
-
- /// The agent is working autonomously toward task completion.
- public static SessionMode Autopilot { get; } = new("autopilot");
-
- /// Returns a value indicating whether two instances are equivalent.
- public static bool operator ==(SessionMode left, SessionMode right) => left.Equals(right);
-
- /// Returns a value indicating whether two instances are not equivalent.
- public static bool operator !=(SessionMode left, SessionMode right) => !(left == right);
-
- ///
- public override bool Equals(object? obj) => obj is SessionMode other && Equals(other);
-
- ///
- public bool Equals(SessionMode 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 SessionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
- {
- return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
- }
-
- ///
- public override void Write(Utf8JsonWriter writer, SessionMode value, JsonSerializerOptions options)
- {
- GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(SessionMode));
- }
- }
-}
-
/// Permission mode for the session.
[Experimental(Diagnostics.Experimental)]
[JsonConverter(typeof(Converter))]
@@ -10443,15 +11424,204 @@ public TaskCompletionOutcome(string value)
public sealed class Converter : JsonConverter
{
///
- public override TaskCompletionOutcome Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ public override TaskCompletionOutcome Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, TaskCompletionOutcome value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskCompletionOutcome));
+ }
+ }
+}
+
+/// Kind of turn for which HydraFusion routing is running.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct FusionTurnKind : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public FusionTurnKind(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// A user-message turn.
+ public static FusionTurnKind User { get; } = new("user");
+
+ /// A conversation-compaction turn.
+ public static FusionTurnKind Compaction { get; } = new("compaction");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(FusionTurnKind left, FusionTurnKind right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(FusionTurnKind left, FusionTurnKind right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is FusionTurnKind other && Equals(other);
+
+ ///
+ public bool Equals(FusionTurnKind 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 FusionTurnKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, FusionTurnKind value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(FusionTurnKind));
+ }
+ }
+}
+
+/// Server-recommended routing behavior for a later HydraFusion turn.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct FusionFollowUpAction : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public FusionFollowUpAction(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// Reuse the durable primary model without routing.
+ public static FusionFollowUpAction ReusePrimary { get; } = new("reuse_primary");
+
+ /// Request a new routing decision.
+ public static FusionFollowUpAction Reroute { get; } = new("reroute");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(FusionFollowUpAction left, FusionFollowUpAction right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(FusionFollowUpAction left, FusionFollowUpAction right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is FusionFollowUpAction other && Equals(other);
+
+ ///
+ public bool Equals(FusionFollowUpAction 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 FusionFollowUpAction Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, FusionFollowUpAction value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(FusionFollowUpAction));
+ }
+ }
+}
+
+/// Validated HydraFusion execution pattern.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct FusionPattern : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public FusionPattern(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// Run one primary solver phase.
+ public static FusionPattern Single { get; } = new("single");
+
+ /// Run a primary phase, a judge, and an optional repair.
+ public static FusionPattern Cascade { get; } = new("cascade");
+
+ /// Run a primary draft, a read-only critique, and a revision.
+ public static FusionPattern Critique { get; } = new("critique");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(FusionPattern left, FusionPattern right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(FusionPattern left, FusionPattern right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is FusionPattern other && Equals(other);
+
+ ///
+ public bool Equals(FusionPattern 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 FusionPattern Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
}
///
- public override void Write(Utf8JsonWriter writer, TaskCompletionOutcome value, JsonSerializerOptions options)
+ public override void Write(Utf8JsonWriter writer, FusionPattern value, JsonSerializerOptions options)
{
- GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(TaskCompletionOutcome));
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(FusionPattern));
}
}
}
@@ -10901,6 +12071,275 @@ public override void Write(Utf8JsonWriter writer, ModelCallFailureTransport valu
}
}
+/// Conversation scope in which a HydraFusion phase executes.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct FusionConversationScope : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public FusionConversationScope(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// Canonical root conversation history.
+ public static FusionConversationScope Root { get; } = new("root");
+
+ /// Isolated read-only review history that does not enter the root conversation.
+ public static FusionConversationScope Review { get; } = new("review");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(FusionConversationScope left, FusionConversationScope right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(FusionConversationScope left, FusionConversationScope right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is FusionConversationScope other && Equals(other);
+
+ ///
+ public bool Equals(FusionConversationScope 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 FusionConversationScope Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, FusionConversationScope value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(FusionConversationScope));
+ }
+ }
+}
+
+/// HydraFusion phase kind.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct FusionPhaseKind : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public FusionPhaseKind(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// Primary solver phase.
+ public static FusionPhaseKind Primary { get; } = new("primary");
+
+ /// Read-only cascade judge phase.
+ public static FusionPhaseKind Judge { get; } = new("judge");
+
+ /// Cascade repair phase.
+ public static FusionPhaseKind Repair { get; } = new("repair");
+
+ /// Initial critique-pattern draft phase.
+ public static FusionPhaseKind Draft { get; } = new("draft");
+
+ /// Read-only critique phase.
+ public static FusionPhaseKind Critic { get; } = new("critic");
+
+ /// Critique-pattern revision phase.
+ public static FusionPhaseKind Revision { get; } = new("revision");
+
+ /// Follow-up phase continuing from the resolved model.
+ public static FusionPhaseKind FollowUp { get; } = new("follow_up");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(FusionPhaseKind left, FusionPhaseKind right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(FusionPhaseKind left, FusionPhaseKind right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is FusionPhaseKind other && Equals(other);
+
+ ///
+ public bool Equals(FusionPhaseKind 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 FusionPhaseKind Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, FusionPhaseKind value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(FusionPhaseKind));
+ }
+ }
+}
+
+/// How a durable phase checkpoint contributes its exact message to canonical root history.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct FusionProjectionMode : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public FusionProjectionMode(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// Append the exact root message immediately.
+ public static FusionProjectionMode Append { get; } = new("append");
+
+ /// Hold a terminal message outside canonical history until the final commit selects it.
+ public static FusionProjectionMode Staged { get; } = new("staged");
+
+ /// Do not project the checkpoint into root history.
+ public static FusionProjectionMode None { get; } = new("none");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(FusionProjectionMode left, FusionProjectionMode right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(FusionProjectionMode left, FusionProjectionMode right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is FusionProjectionMode other && Equals(other);
+
+ ///
+ public bool Equals(FusionProjectionMode 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 FusionProjectionMode Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, FusionProjectionMode value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(FusionProjectionMode));
+ }
+ }
+}
+
+/// Durable outcome status of a HydraFusion phase.
+[Experimental(Diagnostics.Experimental)]
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct FusionPhaseStatus : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public FusionPhaseStatus(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// The phase completed successfully.
+ public static FusionPhaseStatus Succeeded { get; } = new("succeeded");
+
+ /// The phase failed.
+ public static FusionPhaseStatus Failed { get; } = new("failed");
+
+ /// The phase was cancelled.
+ public static FusionPhaseStatus Cancelled { get; } = new("cancelled");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(FusionPhaseStatus left, FusionPhaseStatus right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(FusionPhaseStatus left, FusionPhaseStatus right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is FusionPhaseStatus other && Equals(other);
+
+ ///
+ public bool Equals(FusionPhaseStatus 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 FusionPhaseStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, FusionPhaseStatus value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(FusionPhaseStatus));
+ }
+ }
+}
+
/// Tool call type: "function" for standard tool calls, "custom" for grammar-based tool calls. Defaults to "function" when absent.
[JsonConverter(typeof(Converter))]
[DebuggerDisplay("{Value,nq}")]
@@ -11027,6 +12466,64 @@ public override void Write(Utf8JsonWriter writer, CitationProvider value, JsonSe
}
}
+/// Hosted program caller type.
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct AssistantMessageToolRequestCallerType : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public AssistantMessageToolRequestCallerType(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// Gets the program value.
+ public static AssistantMessageToolRequestCallerType Program { get; } = new("program");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(AssistantMessageToolRequestCallerType left, AssistantMessageToolRequestCallerType right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(AssistantMessageToolRequestCallerType left, AssistantMessageToolRequestCallerType right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is AssistantMessageToolRequestCallerType other && Equals(other);
+
+ ///
+ public bool Equals(AssistantMessageToolRequestCallerType 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 AssistantMessageToolRequestCallerType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, AssistantMessageToolRequestCallerType value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(AssistantMessageToolRequestCallerType));
+ }
+ }
+}
+
/// API endpoint used for this model call, matching CAPI supported_endpoints vocabulary.
[JsonConverter(typeof(Converter))]
[DebuggerDisplay("{Value,nq}")]
@@ -11341,6 +12838,73 @@ public override void Write(Utf8JsonWriter writer, ModelCallFailureSource value,
}
}
+/// Final outcome of one logical model dispatch after response acceptance processing.
+[JsonConverter(typeof(Converter))]
+[DebuggerDisplay("{Value,nq}")]
+public readonly struct ModelCallFinishedOutcome : IEquatable
+{
+ private readonly string? _value;
+
+ /// Initializes a new instance of the struct.
+ /// The value to associate with this .
+ [JsonConstructor]
+ public ModelCallFinishedOutcome(string value)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(value);
+ _value = value;
+ }
+
+ /// Gets the value associated with this .
+ public string Value => _value ?? string.Empty;
+
+ /// The provider response was accepted for continued agent processing.
+ public static ModelCallFinishedOutcome Success { get; } = new("success");
+
+ /// The dispatch ended with a provider or transport error.
+ public static ModelCallFinishedOutcome Error { get; } = new("error");
+
+ /// The dispatch was cancelled before an accepted response was produced.
+ public static ModelCallFinishedOutcome Cancelled { get; } = new("cancelled");
+
+ /// The provider response was rejected during post-response acceptance processing.
+ public static ModelCallFinishedOutcome Rejected { get; } = new("rejected");
+
+ /// Returns a value indicating whether two instances are equivalent.
+ public static bool operator ==(ModelCallFinishedOutcome left, ModelCallFinishedOutcome right) => left.Equals(right);
+
+ /// Returns a value indicating whether two instances are not equivalent.
+ public static bool operator !=(ModelCallFinishedOutcome left, ModelCallFinishedOutcome right) => !(left == right);
+
+ ///
+ public override bool Equals(object? obj) => obj is ModelCallFinishedOutcome other && Equals(other);
+
+ ///
+ public bool Equals(ModelCallFinishedOutcome 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 ModelCallFinishedOutcome Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ return new(GeneratedStringEnumJson.ReadValue(ref reader, typeToConvert));
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, ModelCallFinishedOutcome value, JsonSerializerOptions options)
+ {
+ GeneratedStringEnumJson.WriteValue(writer, value.Value, typeof(ModelCallFinishedOutcome));
+ }
+ }
+}
+
/// Finite reason code describing why the current turn was aborted.
[JsonConverter(typeof(Converter))]
[DebuggerDisplay("{Value,nq}")]
@@ -13403,6 +14967,9 @@ public ManagedSettingsEnforcedEscalation(string value)
/// Unrestricted URL fetch access.
public static ManagedSettingsEnforcedEscalation UnrestrictedUrls { get; } = new("unrestricted_urls");
+ /// A server-wide MCP "Always Allow" (or `--allow-tool <server>`) blanket that would auto-approve every tool from an MCP server. Capped to per-tool approval; each tool still prompts.
+ public static ManagedSettingsEnforcedEscalation ServerWideMcpApproval { get; } = new("server_wide_mcp_approval");
+
/// Returns a value indicating whether two instances are equivalent.
public static bool operator ==(ManagedSettingsEnforcedEscalation left, ManagedSettingsEnforcedEscalation right) => left.Equals(right);
@@ -14002,6 +15569,12 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu
[JsonSerializable(typeof(AbortEvent))]
[JsonSerializable(typeof(AgentInterruptedData))]
[JsonSerializable(typeof(AgentInterruptedEvent))]
+[JsonSerializable(typeof(AssistantFusionPhaseCompletedData))]
+[JsonSerializable(typeof(AssistantFusionPhaseCompletedEvent))]
+[JsonSerializable(typeof(AssistantFusionPhaseFailedData))]
+[JsonSerializable(typeof(AssistantFusionPhaseFailedEvent))]
+[JsonSerializable(typeof(AssistantFusionPhaseStartedData))]
+[JsonSerializable(typeof(AssistantFusionPhaseStartedEvent))]
[JsonSerializable(typeof(AssistantIdleData))]
[JsonSerializable(typeof(AssistantIdleEvent))]
[JsonSerializable(typeof(AssistantIntentData))]
@@ -14010,10 +15583,12 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu
[JsonSerializable(typeof(AssistantMessageDeltaData))]
[JsonSerializable(typeof(AssistantMessageDeltaEvent))]
[JsonSerializable(typeof(AssistantMessageEvent))]
+[JsonSerializable(typeof(AssistantMessageReasoningBlocks))]
[JsonSerializable(typeof(AssistantMessageServerTools))]
[JsonSerializable(typeof(AssistantMessageStartData))]
[JsonSerializable(typeof(AssistantMessageStartEvent))]
[JsonSerializable(typeof(AssistantMessageToolRequest))]
+[JsonSerializable(typeof(AssistantMessageToolRequestCaller))]
[JsonSerializable(typeof(AssistantReasoningData))]
[JsonSerializable(typeof(AssistantReasoningDeltaData))]
[JsonSerializable(typeof(AssistantReasoningDeltaEvent))]
@@ -14112,6 +15687,11 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu
[JsonSerializable(typeof(FactoryRunStartedEvent))]
[JsonSerializable(typeof(FactoryRunUpdatedData))]
[JsonSerializable(typeof(FactoryRunUpdatedEvent))]
+[JsonSerializable(typeof(FusionAttribution))]
+[JsonSerializable(typeof(FusionFollowUpRecommendation))]
+[JsonSerializable(typeof(FusionPhaseUsage))]
+[JsonSerializable(typeof(FusionScores))]
+[JsonSerializable(typeof(FusionStagedTerminal))]
[JsonSerializable(typeof(GitHubMcpToolConfig))]
[JsonSerializable(typeof(GitHubRepoRef))]
[JsonSerializable(typeof(HandoffRepository))]
@@ -14149,6 +15729,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu
[JsonSerializable(typeof(ModelCallFailureData))]
[JsonSerializable(typeof(ModelCallFailureEvent))]
[JsonSerializable(typeof(ModelCallFailureRequestFingerprint))]
+[JsonSerializable(typeof(ModelCallFinishedData))]
+[JsonSerializable(typeof(ModelCallFinishedEvent))]
[JsonSerializable(typeof(ModelCallStartData))]
[JsonSerializable(typeof(ModelCallStartEvent))]
[JsonSerializable(typeof(OmittedBinaryResult))]
@@ -14249,6 +15831,14 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu
[JsonSerializable(typeof(SessionExtensionsAttachmentsPushedEvent))]
[JsonSerializable(typeof(SessionExtensionsLoadedData))]
[JsonSerializable(typeof(SessionExtensionsLoadedEvent))]
+[JsonSerializable(typeof(SessionFusionCompletedData))]
+[JsonSerializable(typeof(SessionFusionCompletedEvent))]
+[JsonSerializable(typeof(SessionFusionResolvedData))]
+[JsonSerializable(typeof(SessionFusionResolvedEvent))]
+[JsonSerializable(typeof(SessionFusionRouteFailedData))]
+[JsonSerializable(typeof(SessionFusionRouteFailedEvent))]
+[JsonSerializable(typeof(SessionFusionRouteStartedData))]
+[JsonSerializable(typeof(SessionFusionRouteStartedEvent))]
[JsonSerializable(typeof(SessionHandoffData))]
[JsonSerializable(typeof(SessionHandoffEvent))]
[JsonSerializable(typeof(SessionIdleData))]
@@ -14327,6 +15917,8 @@ public override void Write(Utf8JsonWriter writer, ExtensionsLoadedExtensionStatu
[JsonSerializable(typeof(SkillsLoadedSkill))]
[JsonSerializable(typeof(SubagentCompletedData))]
[JsonSerializable(typeof(SubagentCompletedEvent))]
+[JsonSerializable(typeof(SubagentConfiguredData))]
+[JsonSerializable(typeof(SubagentConfiguredEvent))]
[JsonSerializable(typeof(SubagentDeselectedData))]
[JsonSerializable(typeof(SubagentDeselectedEvent))]
[JsonSerializable(typeof(SubagentFailedData))]
diff --git a/dotnet/src/GitHubTokenProvider.cs b/dotnet/src/GitHubTokenProvider.cs
new file mode 100644
index 0000000000..3a79d09662
--- /dev/null
+++ b/dotnet/src/GitHubTokenProvider.cs
@@ -0,0 +1,77 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace GitHub.Copilot;
+
+/// Why the runtime is requesting a GitHub token.
+[Experimental(Diagnostics.Experimental)]
+public enum GitHubTokenRequestReason
+{
+ /// The session needs its initial token.
+ Initial,
+
+ /// The session needs a refreshed token.
+ Refresh,
+}
+
+/// Arguments passed to a session-scoped GitHub token provider.
+[Experimental(Diagnostics.Experimental)]
+public sealed class GitHubTokenProviderArgs
+{
+ /// Gets the effective GitHub host for which a token is needed.
+ public required string Host { get; init; }
+
+ ///
+ /// Gets the session receiving the token, or before a
+ /// cloud session has been assigned an identifier.
+ ///
+ public string? SessionId { get; init; }
+
+ /// Gets whether the runtime needs an initial or refreshed token.
+ public required GitHubTokenRequestReason Reason { get; init; }
+}
+
+/// A GitHub access token returned by a session-scoped provider.
+[Experimental(Diagnostics.Experimental)]
+public sealed class GitHubToken
+{
+ /// Gets or sets the GitHub access token.
+ public required string AccessToken { get; set; }
+
+ /// Gets or sets the OAuth token type. The runtime defaults it to bearer.
+ public string? TokenType { get; set; }
+
+ ///
+ /// Gets or sets the required positive number of seconds remaining when the
+ /// callback completes. Production GitHub tokens typically last eight hours.
+ ///
+ public required long ExpiresIn { get; set; }
+
+ ///
+ public override string ToString()
+ => $"{nameof(GitHubToken)} {{ {nameof(TokenType)} = {TokenType}, {nameof(ExpiresIn)} = {ExpiresIn}, {nameof(AccessToken)} = }}";
+}
+
+/// The result returned by a session-scoped GitHub token provider.
+[Experimental(Diagnostics.Experimental)]
+public sealed class GitHubTokenProviderResult
+{
+ /// Gets whether token acquisition was cancelled.
+ public bool Cancelled { get; private init; }
+
+ /// Gets the acquired token, if acquisition was not cancelled.
+ public GitHubToken? Token { get; private init; }
+
+ /// Creates a successful token result.
+ public static GitHubTokenProviderResult FromToken(GitHubToken token)
+ {
+ ArgumentNullException.ThrowIfNull(token);
+ return new() { Token = token };
+ }
+
+ /// Creates a cancelled result.
+ public static GitHubTokenProviderResult Cancel() => new() { Cancelled = true };
+}
diff --git a/dotnet/src/JsonRpc.cs b/dotnet/src/JsonRpc.cs
index 36289d2e68..c5c444ea70 100644
--- a/dotnet/src/JsonRpc.cs
+++ b/dotnet/src/JsonRpc.cs
@@ -548,7 +548,11 @@ private async Task HandleIncomingMethodAsync(string methodName, JsonElement mess
if (requestId.HasValue)
{
- await SendResultResponseAsync(requestId.Value, result, cancellationToken).ConfigureAwait(false);
+ await SendResultResponseAsync(
+ requestId.Value,
+ result,
+ registration.ResultType,
+ cancellationToken).ConfigureAwait(false);
}
}
catch (Exception ex) when (ex is not OperationCanceledException)
@@ -772,18 +776,20 @@ private static bool TryGetPropertyCaseInsensitive(JsonElement obj, string name,
return doc.RootElement.Clone();
}
- private async Task SendResultResponseAsync(JsonElement id, object? result, CancellationToken cancellationToken)
+ private async Task SendResultResponseAsync(
+ JsonElement id,
+ object? result,
+ Type? declaredResultType,
+ CancellationToken cancellationToken)
{
try
{
- // Convert the result to a JsonElement using the runtime type, looked up via
- // the merged resolver. Source-gen serialization of an `object`-typed property
- // would otherwise have no way to find metadata for the actual response type
- // (e.g. SystemMessageTransformRpcResponse, SessionFsReadFileResult, ...).
+ // Prefer the handler's declared result type so polymorphic base types emit
+ // their discriminator. Fall back to the runtime type for untyped handlers.
JsonElement? resultElement = null;
if (result is not null)
{
- var typeInfo = _serializerOptions.GetTypeInfo(result.GetType());
+ var typeInfo = _serializerOptions.GetTypeInfo(declaredResultType ?? result.GetType());
resultElement = JsonSerializer.SerializeToElement(result, typeInfo);
}
@@ -863,6 +869,11 @@ public MethodRegistration(Delegate handler, bool singleObjectParam)
{
ValueTaskAsTaskMethod = GetMethodFromGenericMethodDefinition(returnType, s_valueTaskAsTask);
TaskResultGetter = GetMethodFromGenericMethodDefinition(ValueTaskAsTaskMethod.ReturnType, s_taskGetResult);
+ ResultType = returnType.GetGenericArguments()[0];
+ }
+ else if (returnType != typeof(void) && returnType != typeof(Task) && returnType != typeof(ValueTask))
+ {
+ ResultType = returnType;
}
}
@@ -871,6 +882,7 @@ public MethodRegistration(Delegate handler, bool singleObjectParam)
public ParameterInfo[] Parameters { get; }
public MethodInfo? ValueTaskAsTaskMethod { get; }
public MethodInfo? TaskResultGetter { get; }
+ public Type? ResultType { get; }
}
private static MethodInfo GetMethodFromGenericMethodDefinition(Type specializedType, MethodInfo genericMethodDefinition)
diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs
index a6fad7dcfa..7d076d14b0 100644
--- a/dotnet/src/Session.cs
+++ b/dotnet/src/Session.cs
@@ -82,6 +82,7 @@ private sealed record EventSubscription(Type EventType, Action Han
private IReadOnlyList _openCanvases = Array.Empty();
private int _isDisposed;
+ private string? _gitHubTokenProviderRegistrationId;
///
/// Channel that serializes event dispatch. enqueues;
@@ -203,6 +204,19 @@ internal void RemoveFromClient()
((ICollection>)_parentClient._sessions).Remove(new(SessionId, this));
}
+ internal void SetGitHubTokenProviderRegistration(string registrationId)
+ {
+ _gitHubTokenProviderRegistrationId = registrationId;
+ }
+
+ internal void ReleaseGitHubTokenProviderRegistration()
+ {
+ if (Interlocked.Exchange(ref _gitHubTokenProviderRegistrationId, null) is { } registrationId)
+ {
+ _parentClient.UnregisterGitHubTokenProvider(registrationId);
+ }
+ }
+
internal void StartProcessingEvents()
{
_ = ProcessEventsAsync();
@@ -357,7 +371,7 @@ void Handler(SessionEvent evt)
}
break;
- case SessionIdleEvent:
+ case SessionIdleEvent idleEvent when idleEvent.Data.Mode != SessionMode.Autopilot:
LoggingHelpers.LogTiming(_logger, LogLevel.Debug, null,
"CopilotSession.SendAndWaitAsync idle received. Elapsed={Elapsed}, SessionId={SessionId}",
totalTimestamp,
@@ -1936,6 +1950,7 @@ await InvokeRpcAsync