diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0c213e61c0..c6959d3089 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @github/copilot-sdk-java +* @github/copilot-sdk diff --git a/.github/scripts/release/test-update-changelog.sh b/.github/scripts/release/test-update-changelog.sh index ade87d6a94..b5fc8486b7 100755 --- a/.github/scripts/release/test-update-changelog.sh +++ b/.github/scripts/release/test-update-changelog.sh @@ -33,10 +33,10 @@ run_test() { if $test_func; then echo -e "${GREEN}PASSED${NC}" - ((passed++)) + passed=$((passed + 1)) else echo -e "${RED}FAILED${NC}" - ((failed++)) + failed=$((failed + 1)) fi } @@ -180,6 +180,71 @@ EOF fi } +# Test 6: Beta-java version format (e.g., 1.0.0-beta-java.N) +test_beta_java_version() { + local test_file="${TEST_DIR}/test6.md" + cat > "$test_file" << 'EOF' +# Changelog + +## [Unreleased] + +### Added +- New feature + +## [1.0.0-beta-java.1] - 2026-05-01 + +[Unreleased]: https://github.com/test/repo/compare/v1.0.0-beta-java.1...HEAD +[1.0.0-beta-java.1]: https://github.com/test/repo/compare/v0.3.0-java.2...v1.0.0-beta-java.1 +[0.3.0-java.2]: https://github.com/test/repo/releases/tag/0.3.0-java.2 +EOF + + CHANGELOG_FILE="$test_file" bash "$UPDATE_SCRIPT" 1.0.0-beta-java.2 > /dev/null 2>&1 + + # The [Unreleased] link should now point to v1.0.0-beta-java.2 + # [1.0.0-beta-java.2] should compare from v1.0.0-beta-java.1 + if grep -q "\[Unreleased\]: https://github.com/test/repo/compare/v1.0.0-beta-java.2...HEAD" "$test_file" && \ + grep -q "\[1.0.0-beta-java.2\]: https://github.com/test/repo/compare/v1.0.0-beta-java.1...v1.0.0-beta-java.2" "$test_file"; then + return 0 + else + return 1 + fi +} + +# Test 7: No duplicate [Unreleased] links when existing [Unreleased] link is present +test_no_duplicate_unreleased_links() { + local test_file="${TEST_DIR}/test7.md" + cat > "$test_file" << 'EOF' +# Changelog + +## [Unreleased] + +### Added +- New feature + +## [1.0.0-beta-java.2] - 2026-05-08 + +## [1.0.0-beta-java.1] - 2026-05-05 + +[Unreleased]: https://github.com/test/repo/compare/v1.0.0-beta-java.2...HEAD +[1.0.0-beta-java.2]: https://github.com/test/repo/compare/v1.0.0-beta-java.1...v1.0.0-beta-java.2 +[1.0.0-beta-java.1]: https://github.com/test/repo/compare/v0.3.0-java.2...v1.0.0-beta-java.1 +[0.3.0-java.2]: https://github.com/test/repo/releases/tag/0.3.0-java.2 +EOF + + CHANGELOG_FILE="$test_file" bash "$UPDATE_SCRIPT" 1.0.0-beta-java.3 > /dev/null 2>&1 + + # Count [Unreleased] link definitions - there should be exactly one + local unreleased_count + unreleased_count=$(grep -c "^\[Unreleased\]:" "$test_file") + if [ "$unreleased_count" -eq 1 ] && \ + grep -q "\[Unreleased\]: https://github.com/test/repo/compare/v1.0.0-beta-java.3...HEAD" "$test_file" && \ + grep -q "\[1.0.0-beta-java.3\]: https://github.com/test/repo/compare/v1.0.0-beta-java.2...v1.0.0-beta-java.3" "$test_file"; then + return 0 + else + return 1 + fi +} + # Run all tests echo "Running CHANGELOG update script tests..." echo "" @@ -189,6 +254,8 @@ run_test "Handle CHANGELOG without Unreleased link" test_no_unreleased_link run_test "Preserve content structure" test_preserve_content run_test "Error handling - no Unreleased section" test_no_unreleased_section run_test "Multiple version handling" test_multiple_versions +run_test "Beta-java version format (e.g., 1.0.0-beta-java.N)" test_beta_java_version +run_test "No duplicate [Unreleased] links when existing link is present" test_no_duplicate_unreleased_links echo "" echo "==========================================" diff --git a/.github/scripts/release/update-changelog.sh b/.github/scripts/release/update-changelog.sh index ff35dec722..e21d03774e 100755 --- a/.github/scripts/release/update-changelog.sh +++ b/.github/scripts/release/update-changelog.sh @@ -45,6 +45,7 @@ BEGIN { links_section = 0 first_version_link = "" repo_url = "" + unreleased_link_handled = 0 } # Track if we are in the links section at the bottom @@ -53,7 +54,7 @@ BEGIN { } # Capture the repository URL from the first version link -links_section && repo_url == "" && /^\[[0-9]+\.[0-9]+\.[0-9]+(-java\.[0-9]+)?\]:/ { +links_section && repo_url == "" && /^\[[0-9]+\.[0-9]+\.[0-9]+(-(beta-)?java(-preview)?\.[0-9]+)?\]:/ { match($0, /(https:\/\/github\.com\/[^\/]+\/[^\/]+)\//, arr) if (arr[1] != "") { repo_url = arr[1] @@ -86,28 +87,30 @@ skip_old_reference_impl && /^[[:space:]]*$/ { next } skip_old_reference_impl && /^> \*\*Reference implementation sync:\*\*/ { next } skip_old_reference_impl && !/^[[:space:]]*$/ && !/^> \*\*Reference implementation sync:\*\*/ { skip_old_reference_impl = 0 } -# Capture the first version link to get the previous version -links_section && first_version_link == "" && /^\[[0-9]+\.[0-9]+\.[0-9]+(-java\.[0-9]+)?\]:/ { - match($0, /\[([0-9]+\.[0-9]+\.[0-9]+(-java\.[0-9]+)?)\]:/, arr) - if (arr[1] != "" && repo_url != "") { - first_version_link = arr[1] - # Insert Unreleased and new version links before first version link - print "[Unreleased]: " repo_url "/compare/v" version "...HEAD" - print "[" version "]: " repo_url "/compare/v" arr[1] "...v" version - } -} - -# Update existing [Unreleased] link if present +# Update existing [Unreleased] link if present (must be checked before the first-version-link block) links_section && /^\[Unreleased\]:/ { # Get the previous version and repo URL from the existing link - match($0, /(https:\/\/github\.com\/[^\/]+\/[^\/]+)\/compare\/v([0-9]+\.[0-9]+\.[0-9]+(-java\.[0-9]+)?)\.\.\.HEAD/, arr) + match($0, /(https:\/\/github\.com\/[^\/]+\/[^\/]+)\/compare\/v([0-9]+\.[0-9]+\.[0-9]+(-(beta-)?java(-preview)?\.[0-9]+)?)\.\.\.HEAD/, arr) if (arr[1] != "" && arr[2] != "") { print "[Unreleased]: " arr[1] "/compare/v" version "...HEAD" print "[" version "]: " arr[1] "/compare/v" arr[2] "...v" version + unreleased_link_handled = 1 next } } +# Capture the first version link to get the previous version +# Only fires if the [Unreleased] link was not already handled above +links_section && first_version_link == "" && !unreleased_link_handled && /^\[[0-9]+\.[0-9]+\.[0-9]+(-(beta-)?java(-preview)?\.[0-9]+)?\]:/ { + match($0, /\[([0-9]+\.[0-9]+\.[0-9]+(-(beta-)?java(-preview)?\.[0-9]+)?)\]:/, arr) + if (arr[1] != "" && repo_url != "") { + first_version_link = arr[1] + # Insert Unreleased and new version links before first version link + print "[Unreleased]: " repo_url "/compare/v" version "...HEAD" + print "[" version "]: " repo_url "/compare/v" arr[1] "...v" version + } +} + # Print all other lines unchanged { print } ' "$CHANGELOG_FILE" > "$TEMP_FILE" diff --git a/.github/workflows/codegen-check.yml b/.github/workflows/codegen-check.yml index b6f161d852..95e9365782 100644 --- a/.github/workflows/codegen-check.yml +++ b/.github/workflows/codegen-check.yml @@ -128,6 +128,9 @@ jobs: run: | ERROR_SUMMARY=$(cat /tmp/error-summary.txt) + # Ensure PR has dependencies label (required by codegen-agentic-fix safe-output) + gh pr edit "$PR_NUMBER" --add-label dependencies + gh workflow run codegen-agentic-fix.lock.yml \ -f branch="$BRANCH" \ -f pr_number="$PR_NUMBER" \ diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index 0d7e4f9a6c..9522a64923 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -24,7 +24,7 @@ jobs: # Install GitHub CLI and gh-aw extension for Copilot Agent interaction - name: Install gh-aw extension - uses: github/gh-aw/actions/setup-cli@19ac811a4a85389c33b15128e1d7b7d4507f814a # v0.71.5 + uses: github/gh-aw/actions/setup-cli@4d44d0e89851a877f4ddc0cb6c0197e42b1016c5 # v0.73.0 with: version: v0.68.3 diff --git a/.github/workflows/publish-maven.yml b/.github/workflows/publish-maven.yml index 99e61ac18f..cc612389b7 100644 --- a/.github/workflows/publish-maven.yml +++ b/.github/workflows/publish-maven.yml @@ -87,8 +87,8 @@ jobs: else # Split version: supports "0.1.32", "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]+(-java(-preview)?\.[0-9]+)?$'; then - echo "Error: RELEASE_VERSION '$RELEASE_VERSION' is invalid. Expected format: M.M.P, M.M.P-java.N, or M.M.P-java-preview.N (e.g., 1.2.3, 1.2.3-java.0, or 1.2.3-java-preview.0)." >&2 + if ! echo "$RELEASE_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-(beta-)?java(-preview)?\.[0-9]+)?$'; then + echo "Error: RELEASE_VERSION '$RELEASE_VERSION' is invalid. Expected format: M.M.P, 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-java.0, 1.2.3-java-preview.0, 1.2.3-beta-java.0, or 1.2.3-beta-java-preview.0)." >&2 exit 1 fi # Extract the base M.M.P portion (before any qualifier) @@ -121,21 +121,21 @@ jobs: # Update CHANGELOG.md with release version and Reference implementation sync hash ./.github/scripts/release/update-changelog.sh "${VERSION}" "${REFERENCE_IMPL_HASH}" - # Update version in README.md (supports versions like 1.0.0, 0.1.32-java.0, and 0.3.0-java-preview.0) - sed -i "s|[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\(-preview\)\{0,1\}\.[0-9][0-9]*\)\{0,1\}|${VERSION}|g" README.md - sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\(-preview\)\{0,1\}\.[0-9][0-9]*\)\{0,1\}|copilot-sdk-java:${VERSION}|g" README.md + # Update version in README.md (supports any version qualifier like -java.N, -java-preview.N, -beta-java.N) + sed -i "s|[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*|${VERSION}|g" README.md + sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*|copilot-sdk-java:${VERSION}|g" README.md # Update snapshot version in README.md DEV_VERSION="${{ steps.versions.outputs.dev_version }}" - sed -i "s|[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\(-preview\)\{0,1\}\.[0-9][0-9]*\)\{0,1\}-SNAPSHOT|${DEV_VERSION}|g" README.md + sed -i "s|[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*-SNAPSHOT|${DEV_VERSION}|g" README.md # Update version in jbang-example.java - sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\(-preview\)\{0,1\}\.[0-9][0-9]*\)\{0,1\}|copilot-sdk-java:${VERSION}|g" jbang-example.java + sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*|copilot-sdk-java:${VERSION}|g" jbang-example.java sed -i 's|copilot-sdk-java:${project\.version}|copilot-sdk-java:'"${VERSION}"'|g' jbang-example.java # Update version in cookbook files (hardcoded for direct GitHub browsing and JBang usage) find src/site/markdown/cookbook -name "*.md" -type f -exec \ - sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\(-preview\)\{0,1\}\.[0-9][0-9]*\)\{0,1\}|copilot-sdk-java:${VERSION}|g" {} \; + sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-[a-z][a-z0-9-]*\.[0-9][0-9]*\)*|copilot-sdk-java:${VERSION}|g" {} \; # Commit the documentation changes before release:prepare (requires clean working directory) git add CHANGELOG.md README.md jbang-example.java src/site/markdown/cookbook/ diff --git a/.github/workflows/update-copilot-dependency.yml b/.github/workflows/update-copilot-dependency.yml index 854661d5bd..d29da6acb7 100644 --- a/.github/workflows/update-copilot-dependency.yml +++ b/.github/workflows/update-copilot-dependency.yml @@ -147,6 +147,7 @@ jobs: if gh pr view "$BRANCH" >/dev/null 2>&1; then echo "Pull request for branch '$BRANCH' already exists; updated branch only." PR_NUMBER=$(gh pr view "$BRANCH" --json number --jq .number) + gh pr edit "$PR_NUMBER" --add-label dependencies else PR_NUMBER=$(gh pr create \ --title "Update @github/copilot to $VERSION" \ diff --git a/.lastmerge b/.lastmerge index b28867a960..600b63a547 100644 --- a/.lastmerge +++ b/.lastmerge @@ -1 +1 @@ -4a0437bb03a0b60a1867f14ae8e3faf053afa5aa +e20f5bef125860accb30c60d1b35109371a77f16 diff --git a/CHANGELOG.md b/CHANGELOG.md index fd49095408..8e174dd216 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased] -> **Reference implementation sync:** [`github/copilot-sdk@4a0437b`](https://github.com/github/copilot-sdk/commit/4a0437bb03a0b60a1867f14ae8e3faf053afa5aa) +> **Reference implementation sync:** [`github/copilot-sdk@e20f5be`](https://github.com/github/copilot-sdk/commit/e20f5bef125860accb30c60d1b35109371a77f16) + +## [1.0.0-beta-java.4] - 2026-05-16 +> **Reference implementation sync:** [`github/copilot-sdk@e20f5be`](https://github.com/github/copilot-sdk/commit/e20f5bef125860accb30c60d1b35109371a77f16) ## [1.0.0-beta-java.3] - 2026-05-11 > **Reference implementation sync:** [`github/copilot-sdk@4a0437b`](https://github.com/github/copilot-sdk/commit/4a0437bb03a0b60a1867f14ae8e3faf053afa5aa) @@ -506,38 +509,13 @@ New types: `GetForegroundSessionResponse`, `SetForegroundSessionResponse` - Pre-commit hook for Spotless code formatting - Comprehensive API documentation -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java-preview.0...HEAD -[0.3.0-java-preview.0]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...v0.3.0-java-preview.0 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java-preview.0...HEAD -[0.3.0-java-preview.0]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...v0.3.0-java-preview.0 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java-preview.1...HEAD -[0.3.0-java-preview.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...v0.3.0-java-preview.1 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v1.0.0-beta-java.1...HEAD -[1.0.0-beta-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java.2...v1.0.0-beta-java.1 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v1.0.0-beta-java.1...HEAD +[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v1.0.0-beta-java.4...HEAD +[1.0.0-beta-java.4]: https://github.com/github/copilot-sdk-java/compare/v1.0.0-beta-java.3...v1.0.0-beta-java.4 +[1.0.0-beta-java.3]: https://github.com/github/copilot-sdk-java/compare/v1.0.0-beta-java.2...v1.0.0-beta-java.3 +[1.0.0-beta-java.2]: https://github.com/github/copilot-sdk-java/compare/v1.0.0-beta-java.1...v1.0.0-beta-java.2 [1.0.0-beta-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java.2...v1.0.0-beta-java.1 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v1.0.0-beta-java.2...HEAD -[1.0.0-beta-java.2]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java.2...v1.0.0-beta-java.2 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v1.0.0-beta-java.3...HEAD -[1.0.0-beta-java.3]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java.2...v1.0.0-beta-java.3 [0.3.0-java.2]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...v0.3.0-java.2 -[0.2.2-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.1...v0.2.2-java.1 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java-preview.0...HEAD -[0.3.0-java-preview.0]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...v0.3.0-java-preview.0 -[0.2.2-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.1...v0.2.2-java.1 -[0.2.1-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.0...v0.2.1-java.1 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java-preview.0...HEAD -[0.3.0-java-preview.0]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...v0.3.0-java-preview.0 -[0.2.2-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.1...v0.2.2-java.1 -[0.2.1-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.0...v0.2.1-java.1 -[0.2.1-java.0]: https://github.com/github/copilot-sdk-java/compare/v0.1.32-java.0...v0.2.1-java.0 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java-preview.0...HEAD -[0.3.0-java-preview.0]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...v0.3.0-java-preview.0 -[0.2.2-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.1...v0.2.2-java.1 -[0.2.1-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.0...v0.2.1-java.1 -[0.2.1-java.0]: https://github.com/github/copilot-sdk-java/compare/v0.1.32-java.0...v0.2.1-java.0 -[0.1.32-java.0]: https://github.com/github/copilot-sdk-java/compare/v1.0.11...v0.1.32-java.0 -[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.3.0-java-preview.0...HEAD +[0.3.0-java-preview.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...v0.3.0-java-preview.1 [0.3.0-java-preview.0]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...v0.3.0-java-preview.0 [0.2.2-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.1...v0.2.2-java.1 [0.2.1-java.1]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.0...v0.2.1-java.1 @@ -555,4 +533,3 @@ New types: `GetForegroundSessionResponse`, `SetForegroundSessionResponse` [1.0.2]: https://github.com/github/copilot-sdk-java/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/github/copilot-sdk-java/compare/1.0.0...v1.0.1 [1.0.0]: https://github.com/github/copilot-sdk-java/releases/tag/1.0.0 - diff --git a/README.md b/README.md index 7107c07573..f2b342dfc3 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build A com.github copilot-sdk-java - 1.0.0-beta-java.1 + 1.0.0-beta-java.4 ``` @@ -53,14 +53,14 @@ Snapshot builds of the next development version are published to Maven Central S com.github copilot-sdk-java - 1.0.0-beta-java.3-SNAPSHOT + 1.0.0-beta-java.5-SNAPSHOT ``` ### Gradle ```groovy -implementation 'com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1' +implementation 'com.github:copilot-sdk-java:1.0.0-beta-java.4' ``` ## Quick Start diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index 9a6f3761b5..fcde1f6c97 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -11,9 +11,9 @@ - + - + diff --git a/docs/adr/adr-002-maven-version-and-reference-implementation-tracking.md b/docs/adr/adr-002-maven-version-and-reference-implementation-tracking.md index 6a0b54f225..248a024c23 100644 --- a/docs/adr/adr-002-maven-version-and-reference-implementation-tracking.md +++ b/docs/adr/adr-002-maven-version-and-reference-implementation-tracking.md @@ -8,7 +8,7 @@ Releases of this implementation track releases of the reference implementation. - Simple number qualifier (0.1.32-0, 0.1.32-1, ...) fails on a subtle but important point: 0.1.32-0 is treated identically to 0.1.32 by Maven (trailing zeros are normalized away), and bare numeric qualifiers are pre-release semantics. Your "first release" would sort before the reference implementation bare version. -- Java and number in the qualifier (0.1.32-java.N +- Java and number in the qualifier (0.1.32-java.N) - java is an unknown qualifier that sorts correctly and accurately describes what it is — the Java-ecosystem release of this version. diff --git a/jbang-example.java b/jbang-example.java index e19da2958f..1c41679cdf 100644 --- a/jbang-example.java +++ b/jbang-example.java @@ -1,5 +1,5 @@ ///usr/bin/env jbang "$0" "$@" ; exit $? -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.generated.SessionUsageInfoEvent; diff --git a/pom.xml b/pom.xml index 1a7d6badc2..a925a81cfd 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ com.github copilot-sdk-java - 1.0.0-beta-java.3 + 1.0.0-beta-java.4 jar GitHub Copilot SDK :: Java @@ -33,7 +33,7 @@ scm:git:https://github.com/github/copilot-sdk-java.git scm:git:https://github.com/github/copilot-sdk-java.git https://github.com/github/copilot-sdk-java - v1.0.0-beta-java.3 + v1.0.0-beta-java.4 @@ -94,7 +94,7 @@ reference-impl-sync workflow and deal with the subsequent PR. --> - ^1.0.44-3 + ^1.0.48 @@ -172,13 +172,6 @@ org.apache.maven.plugins maven-jar-plugin 3.5.0 - - - - com.github.copilot.sdk.java - - - diff --git a/scripts/codegen/java.ts b/scripts/codegen/java.ts index 909fe296c6..0a96ab9f14 100644 --- a/scripts/codegen/java.ts +++ b/scripts/codegen/java.ts @@ -43,7 +43,7 @@ function toCamelCase(name: string): string { } function toEnumConstant(value: string): string { - return value.toUpperCase().replace(/[-. ]/g, "_"); + return value.toUpperCase().replace(/[-. /:]/g, "_").replace(/^_+/, "").replace(/_+/g, "_"); } // ── Schema path resolution ─────────────────────────────────────────────────── diff --git a/scripts/codegen/package-lock.json b/scripts/codegen/package-lock.json index 0846fc42e6..36d7689a71 100644 --- a/scripts/codegen/package-lock.json +++ b/scripts/codegen/package-lock.json @@ -6,7 +6,7 @@ "": { "name": "copilot-sdk-java-codegen", "dependencies": { - "@github/copilot": "^1.0.44-3", + "@github/copilot": "^1.0.48", "json-schema": "^0.4.0", "tsx": "^4.20.6" } @@ -428,26 +428,26 @@ } }, "node_modules/@github/copilot": { - "version": "1.0.45", - "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.45.tgz", - "integrity": "sha512-2QADgQcw/d0GFqTq2+nHwX152ZRvZxW0CHONG5d1RCs6YJtdr/GdbnMYYeRH2BiBIhnfkcvF50ImCRvsS5Tnwg==", + "version": "1.0.48", + "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.48.tgz", + "integrity": "sha512-U5SzyTEq376UU9A4Sd3TEKz+Y2nRUd90cLO4Hc1otaB8yFSy9Ur2UVGcI2/wCoodL3a39k6WbdgNzFxr0gWFRQ==", "license": "SEE LICENSE IN LICENSE.md", "bin": { "copilot": "npm-loader.js" }, "optionalDependencies": { - "@github/copilot-darwin-arm64": "1.0.45", - "@github/copilot-darwin-x64": "1.0.45", - "@github/copilot-linux-arm64": "1.0.45", - "@github/copilot-linux-x64": "1.0.45", - "@github/copilot-win32-arm64": "1.0.45", - "@github/copilot-win32-x64": "1.0.45" + "@github/copilot-darwin-arm64": "1.0.48", + "@github/copilot-darwin-x64": "1.0.48", + "@github/copilot-linux-arm64": "1.0.48", + "@github/copilot-linux-x64": "1.0.48", + "@github/copilot-win32-arm64": "1.0.48", + "@github/copilot-win32-x64": "1.0.48" } }, "node_modules/@github/copilot-darwin-arm64": { - "version": "1.0.45", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.45.tgz", - "integrity": "sha512-gCJy1nOIWL5lpLFJTRk2Kz7bS30emkA4p4gM+PJ5/dOwNRBOyUO0/2f03/m5vYL4DNd/T47cFIN6s82gISAIYQ==", + "version": "1.0.48", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.48.tgz", + "integrity": "sha512-82MLoMQwPVVFM8EYssihFxSEPUYtZADE8rMzQ3jG9HgRg2qjQSfnHQS1mKe64dlXswZUK/onw6/8kjnW5I4pPg==", "cpu": [ "arm64" ], @@ -461,9 +461,9 @@ } }, "node_modules/@github/copilot-darwin-x64": { - "version": "1.0.45", - "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.45.tgz", - "integrity": "sha512-nLzC7C0i/WAY+4FukHuONBDNeKUAqBBab3n36aEdpqxVDP5h2Tbzg2yShqav2blR7KDJL7YMcYTVFxmwfQj+yQ==", + "version": "1.0.48", + "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.48.tgz", + "integrity": "sha512-1VQ5r5F0h8GwboXmZTcutqcJT+iCpPXAF27QqodmpKEvW9aYfG8g9X2kFJOzDZoX+SA3Uaka9qXdYKF2xT6Uog==", "cpu": [ "x64" ], @@ -477,9 +477,9 @@ } }, "node_modules/@github/copilot-linux-arm64": { - "version": "1.0.45", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.45.tgz", - "integrity": "sha512-MdRNZUNMrI0dpQ+DiDoZQ7AbitQp9eN7ir176Za2Kf7dkUxPwmio32yhRbBS81McU6vBw8cCzEZviwv/jc8buQ==", + "version": "1.0.48", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.48.tgz", + "integrity": "sha512-PmsGnb0DZlI+Bf53l9HM1PAHHkUcMyB4y8v/7tnC/jDOV5dGF124n0HnDNfJLOLiJGiQGodthIif6QtPaAxpeA==", "cpu": [ "arm64" ], @@ -493,9 +493,9 @@ } }, "node_modules/@github/copilot-linux-x64": { - "version": "1.0.45", - "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.45.tgz", - "integrity": "sha512-xSRUjWA+wrSSjktJSjNtiS/47Cy0PviPejj7RUmtChsPfDJB8wW2iZ6NfpdiAomtxAz5xx4AjbjT1I4b1FqnwA==", + "version": "1.0.48", + "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.48.tgz", + "integrity": "sha512-b2cc4euSlke9fYHXXsS2EL9UYbctN0h4lZvtAcKUDY+RCnpYAQOVBZK+c1R9dQrtsT6Z/yUv7PuFPSs8qdtc2Q==", "cpu": [ "x64" ], @@ -509,9 +509,9 @@ } }, "node_modules/@github/copilot-win32-arm64": { - "version": "1.0.45", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.45.tgz", - "integrity": "sha512-lhcTlKs7MWMzIXv21hUSpL4aFW49jqVhNrQKaB8sYk2nzvGRJvNwTcBS1Tn5ndXlPzQ9P/p9B6B5uwwmZ1vHHw==", + "version": "1.0.48", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.48.tgz", + "integrity": "sha512-VEEOwddtpJ3DTbXGhnK6K8im4ofl9m08q1m/K++sNvWV8wkkOSOQBTiPdyUsuU/TXAoFhb8tZMIJv+6NnMBtMw==", "cpu": [ "arm64" ], @@ -525,9 +525,9 @@ } }, "node_modules/@github/copilot-win32-x64": { - "version": "1.0.45", - "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.45.tgz", - "integrity": "sha512-XYZ983NQmooVr/n+pCnHIorBmf1hd3o1rMlSAodwG/VFlQaydGoOs1F1NntxWBoFAND+eM6N4PZfw8M8sRayfA==", + "version": "1.0.48", + "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.48.tgz", + "integrity": "sha512-93BzvXLPHTyy1gWBXQY/IWIHor4IAwZuuo7/obG80/Qa6U0WeaN9slz/FBJvrsgVNrrRfEID5Xm3At+S6Kj67Q==", "cpu": [ "x64" ], diff --git a/scripts/codegen/package.json b/scripts/codegen/package.json index 6dde795791..82454e774e 100644 --- a/scripts/codegen/package.json +++ b/scripts/codegen/package.json @@ -7,7 +7,7 @@ "generate:java": "tsx java.ts" }, "dependencies": { - "@github/copilot": "^1.0.44-3", + "@github/copilot": "^1.0.48", "json-schema": "^0.4.0", "tsx": "^4.20.6" } diff --git a/src/generated/java/com/github/copilot/sdk/generated/AssistantUsageApiEndpoint.java b/src/generated/java/com/github/copilot/sdk/generated/AssistantUsageApiEndpoint.java new file mode 100644 index 0000000000..e69e4ef868 --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/AssistantUsageApiEndpoint.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.sdk.generated; + +import javax.annotation.processing.Generated; + +/** + * API endpoint used for this model call, matching CAPI supported_endpoints vocabulary + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum AssistantUsageApiEndpoint { + /** The {@code /chat/completions} variant. */ + CHAT_COMPLETIONS("/chat/completions"), + /** The {@code /v1/messages} variant. */ + V1_MESSAGES("/v1/messages"), + /** The {@code /responses} variant. */ + RESPONSES("/responses"), + /** The {@code ws:/responses} variant. */ + WS_RESPONSES("ws:/responses"); + + private final String value; + AssistantUsageApiEndpoint(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static AssistantUsageApiEndpoint fromValue(String value) { + for (AssistantUsageApiEndpoint v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown AssistantUsageApiEndpoint value: " + value); + } +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/AssistantUsageEvent.java b/src/generated/java/com/github/copilot/sdk/generated/AssistantUsageEvent.java index 4613d520e7..ba97e03958 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/AssistantUsageEvent.java +++ b/src/generated/java/com/github/copilot/sdk/generated/AssistantUsageEvent.java @@ -62,6 +62,8 @@ public record AssistantUsageEventData( @JsonProperty("apiCallId") String apiCallId, /** GitHub request tracing ID (x-github-request-id header) for server-side log correlation */ @JsonProperty("providerCallId") String providerCallId, + /** API endpoint used for this model call, matching CAPI supported_endpoints vocabulary */ + @JsonProperty("apiEndpoint") AssistantUsageApiEndpoint apiEndpoint, /** Parent tool call ID when this usage originates from a sub-agent */ @JsonProperty("parentToolCallId") String parentToolCallId, /** Per-quota resource usage snapshots, keyed by quota identifier */ diff --git a/src/generated/java/com/github/copilot/sdk/generated/SessionCustomNotificationEvent.java b/src/generated/java/com/github/copilot/sdk/generated/SessionCustomNotificationEvent.java new file mode 100644 index 0000000000..8d3e3b68ac --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/SessionCustomNotificationEvent.java @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: session-events.schema.json + +package com.github.copilot.sdk.generated; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Map; +import javax.annotation.processing.Generated; + +/** + * The {@code session.custom_notification} session event. + * + * @since 1.0.0 + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public final class SessionCustomNotificationEvent extends SessionEvent { + + @Override + public String getType() { return "session.custom_notification"; } + + @JsonProperty("data") + private SessionCustomNotificationEventData data; + + public SessionCustomNotificationEventData getData() { return data; } + public void setData(SessionCustomNotificationEventData data) { this.data = data; } + + /** Data payload for {@link SessionCustomNotificationEvent}. */ + @JsonIgnoreProperties(ignoreUnknown = true) + @JsonInclude(JsonInclude.Include.NON_NULL) + public record SessionCustomNotificationEventData( + /** Namespace for the custom notification producer */ + @JsonProperty("source") String source, + /** Source-defined custom notification name */ + @JsonProperty("name") String name, + /** Optional source-defined payload schema version */ + @JsonProperty("version") Long version, + /** Optional source-defined string identifiers describing the payload subject */ + @JsonProperty("subject") Map subject, + /** Source-defined JSON payload for the custom notification */ + @JsonProperty("payload") Object payload + ) { + } +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/SessionErrorEvent.java b/src/generated/java/com/github/copilot/sdk/generated/SessionErrorEvent.java index ecb85aacf0..5c174c4357 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/SessionErrorEvent.java +++ b/src/generated/java/com/github/copilot/sdk/generated/SessionErrorEvent.java @@ -37,7 +37,7 @@ public final class SessionErrorEvent extends SessionEvent { public record SessionErrorEventData( /** Category of error (e.g., "authentication", "authorization", "quota", "rate_limit", "context_limit", "query") */ @JsonProperty("errorType") String errorType, - /** Fine-grained error code from the upstream provider, when available. For `errorType: "rate_limit"`, this is one of the `RateLimitErrorCode` values (e.g., `"user_weekly_rate_limited"`, `"user_global_rate_limited"`, `"rate_limited"`, `"user_model_rate_limited"`, `"integration_rate_limited"`). */ + /** Fine-grained error code from the upstream provider, when available. For `errorType: "rate_limit"`, this is one of the `RateLimitErrorCode` values (e.g., `"user_weekly_rate_limited"`, `"user_global_rate_limited"`, `"rate_limited"`, `"user_model_rate_limited"`, `"integration_rate_limited"`). For `errorType: "quota"`, this is the CAPI quota error code (e.g., `"quota_exceeded"`, `"session_quota_exceeded"`, `"billing_not_configured"`). */ @JsonProperty("errorCode") String errorCode, /** Only set on `errorType: "rate_limit"`. When `true`, the runtime will follow this error with an `auto_mode_switch.requested` event (or silently switch if `continueOnAutoMode` is enabled). UI clients can use this flag to suppress duplicate rendering of the rate-limit error when they show their own auto-mode-switch prompt. */ @JsonProperty("eligibleForAutoSwitch") Boolean eligibleForAutoSwitch, diff --git a/src/generated/java/com/github/copilot/sdk/generated/SessionEvent.java b/src/generated/java/com/github/copilot/sdk/generated/SessionEvent.java index 0f348fe863..2181be1972 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/SessionEvent.java +++ b/src/generated/java/com/github/copilot/sdk/generated/SessionEvent.java @@ -87,6 +87,7 @@ @JsonSubTypes.Type(value = SamplingCompletedEvent.class, name = "sampling.completed"), @JsonSubTypes.Type(value = McpOauthRequiredEvent.class, name = "mcp.oauth_required"), @JsonSubTypes.Type(value = McpOauthCompletedEvent.class, name = "mcp.oauth_completed"), + @JsonSubTypes.Type(value = SessionCustomNotificationEvent.class, name = "session.custom_notification"), @JsonSubTypes.Type(value = ExternalToolRequestedEvent.class, name = "external_tool.requested"), @JsonSubTypes.Type(value = ExternalToolCompletedEvent.class, name = "external_tool.completed"), @JsonSubTypes.Type(value = CommandQueuedEvent.class, name = "command.queued"), @@ -170,6 +171,7 @@ public abstract sealed class SessionEvent permits SamplingCompletedEvent, McpOauthRequiredEvent, McpOauthCompletedEvent, + SessionCustomNotificationEvent, ExternalToolRequestedEvent, ExternalToolCompletedEvent, CommandQueuedEvent, diff --git a/src/generated/java/com/github/copilot/sdk/generated/SessionScheduleCreatedEvent.java b/src/generated/java/com/github/copilot/sdk/generated/SessionScheduleCreatedEvent.java index aba8650c84..bf0be67caf 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/SessionScheduleCreatedEvent.java +++ b/src/generated/java/com/github/copilot/sdk/generated/SessionScheduleCreatedEvent.java @@ -40,7 +40,9 @@ public record SessionScheduleCreatedEventData( /** Interval between ticks in milliseconds */ @JsonProperty("intervalMs") Long intervalMs, /** Prompt text that gets enqueued on every tick */ - @JsonProperty("prompt") String prompt + @JsonProperty("prompt") String prompt, + /** Whether the schedule re-arms after each tick (`/every`) or fires once (`/after`) */ + @JsonProperty("recurring") Boolean recurring ) { } } diff --git a/src/generated/java/com/github/copilot/sdk/generated/UserMessageEvent.java b/src/generated/java/com/github/copilot/sdk/generated/UserMessageEvent.java index ec5f382463..fa680d3e64 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/UserMessageEvent.java +++ b/src/generated/java/com/github/copilot/sdk/generated/UserMessageEvent.java @@ -50,6 +50,8 @@ public record UserMessageEventData( @JsonProperty("source") String source, /** The agent mode that was active when this message was sent */ @JsonProperty("agentMode") UserMessageAgentMode agentMode, + /** True when this user message was auto-injected by autopilot's continuation loop rather than typed by the user; used to distinguish autopilot-driven turns in telemetry. */ + @JsonProperty("isAutopilotContinuation") Boolean isAutopilotContinuation, /** CAPI interaction ID for correlating this user message with its turn */ @JsonProperty("interactionId") String interactionId, /** Parent agent task ID for background telemetry correlated to this user turn */ diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/Model.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/Model.java index 9ba457cecb..9bda6a3007 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/rpc/Model.java +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/Model.java @@ -30,6 +30,10 @@ public record Model( /** Supported reasoning effort levels (only present if model supports reasoning effort) */ @JsonProperty("supportedReasoningEfforts") List supportedReasoningEfforts, /** Default reasoning effort level (only present if model supports reasoning effort) */ - @JsonProperty("defaultReasoningEffort") String defaultReasoningEffort + @JsonProperty("defaultReasoningEffort") String defaultReasoningEffort, + /** Model capability category for grouping in the model picker */ + @JsonProperty("modelPickerCategory") ModelPickerCategory modelPickerCategory, + /** Relative cost tier for token-based billing users */ + @JsonProperty("modelPickerPriceCategory") ModelPickerPriceCategory modelPickerPriceCategory ) { } diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelBilling.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelBilling.java index 656f5383d5..9e634bb79a 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelBilling.java +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelBilling.java @@ -22,6 +22,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record ModelBilling( /** Billing cost multiplier relative to the base rate */ - @JsonProperty("multiplier") Double multiplier + @JsonProperty("multiplier") Double multiplier, + /** Token-level pricing information for this model */ + @JsonProperty("tokenPrices") ModelBillingTokenPrices tokenPrices ) { } diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelBillingTokenPrices.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelBillingTokenPrices.java new file mode 100644 index 0000000000..34005daf1b --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelBillingTokenPrices.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Token-level pricing information for this model + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record ModelBillingTokenPrices( + /** Price per billing batch of input tokens in nano-AIUs (1 nano-AIU = 0.000000001 AIU, 1 AIU = $0.01 USD) */ + @JsonProperty("inputPrice") Long inputPrice, + /** Price per billing batch of output tokens in nano-AIUs (1 nano-AIU = 0.000000001 AIU, 1 AIU = $0.01 USD) */ + @JsonProperty("outputPrice") Long outputPrice, + /** Price per billing batch of cached tokens in nano-AIUs (1 nano-AIU = 0.000000001 AIU, 1 AIU = $0.01 USD) */ + @JsonProperty("cachePrice") Long cachePrice, + /** Number of tokens per standard billing batch */ + @JsonProperty("batchSize") Long batchSize +) { +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelPickerCategory.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelPickerCategory.java new file mode 100644 index 0000000000..ba0bdddfd1 --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelPickerCategory.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Model capability category for grouping in the model picker + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelPickerCategory { + /** The {@code lightweight} variant. */ + LIGHTWEIGHT("lightweight"), + /** The {@code versatile} variant. */ + VERSATILE("versatile"), + /** The {@code powerful} variant. */ + POWERFUL("powerful"); + + private final String value; + ModelPickerCategory(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelPickerCategory fromValue(String value) { + for (ModelPickerCategory v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelPickerCategory value: " + value); + } +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelPickerPriceCategory.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelPickerPriceCategory.java new file mode 100644 index 0000000000..cf722e4968 --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/ModelPickerPriceCategory.java @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Relative cost tier for token-based billing users + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum ModelPickerPriceCategory { + /** The {@code low} variant. */ + LOW("low"), + /** The {@code medium} variant. */ + MEDIUM("medium"), + /** The {@code high} variant. */ + HIGH("high"), + /** The {@code very_high} variant. */ + VERY_HIGH("very_high"); + + private final String value; + ModelPickerPriceCategory(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static ModelPickerPriceCategory fromValue(String value) { + for (ModelPickerPriceCategory v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown ModelPickerPriceCategory value: " + value); + } +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/RemoteSessionMode.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/RemoteSessionMode.java new file mode 100644 index 0000000000..4f659bb6b6 --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/RemoteSessionMode.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Per-session remote mode. "off" disables remote, "export" exports session events to Mission Control without enabling remote steering, "on" enables both export and remote steering. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum RemoteSessionMode { + /** The {@code off} variant. */ + OFF("off"), + /** The {@code export} variant. */ + EXPORT("export"), + /** The {@code on} variant. */ + ON("on"); + + private final String value; + RemoteSessionMode(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static RemoteSessionMode fromValue(String value) { + for (RemoteSessionMode v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown RemoteSessionMode value: " + value); + } +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsApi.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsApi.java index 0d3599a1cc..6366af794a 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsApi.java +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsApi.java @@ -29,6 +29,27 @@ public final class SessionCommandsApi { this.sessionId = sessionId; } + /** + * Invokes {@code session.commands.list}. + * @since 1.0.0 + */ + public CompletableFuture list() { + return caller.invoke("session.commands.list", java.util.Map.of("sessionId", this.sessionId), SessionCommandsListResult.class); + } + + /** + * Invokes {@code session.commands.invoke}. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. + * @since 1.0.0 + */ + public CompletableFuture invoke(SessionCommandsInvokeParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.commands.invoke", _p, Void.class); + } + /** * Invokes {@code session.commands.handlePendingCommand}. *

diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsInvokeParams.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsInvokeParams.java new file mode 100644 index 0000000000..141ec9524d --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsInvokeParams.java @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Request parameters for the {@code session.commands.invoke} RPC method. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionCommandsInvokeParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId, + /** Command name. Leading slashes are stripped and the name is matched case-insensitively. */ + @JsonProperty("name") String name, + /** Raw input after the command name */ + @JsonProperty("input") String input +) { +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListParams.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListParams.java new file mode 100644 index 0000000000..dd51f0e768 --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListParams.java @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Request parameters for the {@code session.commands.list} RPC method. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionCommandsListParams( + /** Target session identifier */ + @JsonProperty("sessionId") String sessionId +) { +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListResult.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListResult.java new file mode 100644 index 0000000000..e819ba64c1 --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsListResult.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +/** + * Result for the {@code session.commands.list} RPC method. + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SessionCommandsListResult( + /** Commands available in this session */ + @JsonProperty("commands") List commands +) { +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteApi.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteApi.java index df458acda5..790e1a7255 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteApi.java +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteApi.java @@ -18,6 +18,8 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") public final class SessionRemoteApi { + private static final com.fasterxml.jackson.databind.ObjectMapper MAPPER = RpcMapper.INSTANCE; + private final RpcCaller caller; private final String sessionId; @@ -29,12 +31,17 @@ public final class SessionRemoteApi { /** * Invokes {@code session.remote.enable}. + *

+ * Note: the {@code sessionId} field in the params record is overridden + * by the session-scoped wrapper; any value provided is ignored. * * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ - public CompletableFuture enable() { - return caller.invoke("session.remote.enable", java.util.Map.of("sessionId", this.sessionId), SessionRemoteEnableResult.class); + public CompletableFuture enable(SessionRemoteEnableParams params) { + com.fasterxml.jackson.databind.node.ObjectNode _p = MAPPER.valueToTree(params); + _p.put("sessionId", this.sessionId); + return caller.invoke("session.remote.enable", _p, SessionRemoteEnableResult.class); } /** diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteEnableParams.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteEnableParams.java index d8b7917b78..aa1fff7a23 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteEnableParams.java +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionRemoteEnableParams.java @@ -22,6 +22,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record SessionRemoteEnableParams( /** Target session identifier */ - @JsonProperty("sessionId") String sessionId + @JsonProperty("sessionId") String sessionId, + /** Per-session remote mode. "off" disables remote, "export" exports session events to Mission Control without enabling remote steering, "on" enables both export and remote steering. */ + @JsonProperty("mode") RemoteSessionMode mode ) { } diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsApi.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsApi.java index b32419a3e6..6f46d19d75 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsApi.java +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsApi.java @@ -75,8 +75,8 @@ public CompletableFuture disable(SessionSkillsDisableParams params) { * @apiNote This method is experimental and may change in a future version. * @since 1.0.0 */ - public CompletableFuture reload() { - return caller.invoke("session.skills.reload", java.util.Map.of("sessionId", this.sessionId), Void.class); + public CompletableFuture reload() { + return caller.invoke("session.skills.reload", java.util.Map.of("sessionId", this.sessionId), SessionSkillsReloadResult.class); } } diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsReloadResult.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsReloadResult.java index 1bfca46e1a..1333d57cc1 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsReloadResult.java +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionSkillsReloadResult.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; import javax.annotation.processing.Generated; /** @@ -20,5 +21,10 @@ @javax.annotation.processing.Generated("copilot-sdk-codegen") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) -public record SessionSkillsReloadResult() { +public record SessionSkillsReloadResult( + /** Warnings emitted while loading skills (e.g. skills that loaded but had issues) */ + @JsonProperty("warnings") List warnings, + /** Errors emitted while loading skills (e.g. skills that failed to load entirely) */ + @JsonProperty("errors") List errors +) { } diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkParams.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkParams.java index 06ea46c932..644be05ee0 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkParams.java +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkParams.java @@ -24,6 +24,8 @@ public record SessionsForkParams( /** Source session ID to fork from */ @JsonProperty("sessionId") String sessionId, /** Optional event ID boundary. When provided, the fork includes only events before this ID (exclusive). When omitted, all events are included. */ - @JsonProperty("toEventId") String toEventId + @JsonProperty("toEventId") String toEventId, + /** Optional friendly name to assign to the forked session. */ + @JsonProperty("name") String name ) { } diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkResult.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkResult.java index 911574f568..77543916ec 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkResult.java +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SessionsForkResult.java @@ -22,6 +22,8 @@ @JsonIgnoreProperties(ignoreUnknown = true) public record SessionsForkResult( /** The new forked session's ID */ - @JsonProperty("sessionId") String sessionId + @JsonProperty("sessionId") String sessionId, + /** Friendly name assigned to the forked session, if any. */ + @JsonProperty("name") String name ) { } diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInfo.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInfo.java new file mode 100644 index 0000000000..e494f4894c --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInfo.java @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; +import javax.annotation.processing.Generated; + +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SlashCommandInfo( + /** Canonical command name without a leading slash */ + @JsonProperty("name") String name, + /** Canonical aliases without leading slashes */ + @JsonProperty("aliases") List aliases, + /** Human-readable command description */ + @JsonProperty("description") String description, + /** Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command */ + @JsonProperty("kind") SlashCommandKind kind, + /** Optional unstructured input hint */ + @JsonProperty("input") SlashCommandInput input, + /** Whether the command may run while an agent turn is active */ + @JsonProperty("allowDuringAgentExecution") Boolean allowDuringAgentExecution, + /** Whether the command is experimental */ + @JsonProperty("experimental") Boolean experimental +) { +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInput.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInput.java new file mode 100644 index 0000000000..186dec5a81 --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInput.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.annotation.processing.Generated; + +/** + * Optional unstructured input hint + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public record SlashCommandInput( + /** Hint to display when command input has not been provided */ + @JsonProperty("hint") String hint, + /** When true, the command requires non-empty input; clients should render the input hint as required */ + @JsonProperty("required") Boolean required, + /** Optional completion hint for the input (e.g. 'directory' for filesystem path completion) */ + @JsonProperty("completion") SlashCommandInputCompletion completion, + /** When true, clients should pass the full text after the command name as a single argument rather than splitting on whitespace */ + @JsonProperty("preserveMultilineInput") Boolean preserveMultilineInput +) { +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInputCompletion.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInputCompletion.java new file mode 100644 index 0000000000..c192fa9c0f --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandInputCompletion.java @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Optional completion hint for the input (e.g. 'directory' for filesystem path completion) + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum SlashCommandInputCompletion { + /** The {@code directory} variant. */ + DIRECTORY("directory"); + + private final String value; + SlashCommandInputCompletion(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static SlashCommandInputCompletion fromValue(String value) { + for (SlashCommandInputCompletion v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown SlashCommandInputCompletion value: " + value); + } +} diff --git a/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandKind.java b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandKind.java new file mode 100644 index 0000000000..1f08c4efcd --- /dev/null +++ b/src/generated/java/com/github/copilot/sdk/generated/rpc/SlashCommandKind.java @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +// AUTO-GENERATED FILE - DO NOT EDIT +// Generated from: api.schema.json + +package com.github.copilot.sdk.generated.rpc; + +import javax.annotation.processing.Generated; + +/** + * Coarse command category for grouping and behavior: runtime built-in, skill-backed command, or SDK/client-owned command + * + * @since 1.0.0 + */ +@javax.annotation.processing.Generated("copilot-sdk-codegen") +public enum SlashCommandKind { + /** The {@code builtin} variant. */ + BUILTIN("builtin"), + /** The {@code skill} variant. */ + SKILL("skill"), + /** The {@code client} variant. */ + CLIENT("client"); + + private final String value; + SlashCommandKind(String value) { this.value = value; } + @com.fasterxml.jackson.annotation.JsonValue + public String getValue() { return value; } + @com.fasterxml.jackson.annotation.JsonCreator + public static SlashCommandKind fromValue(String value) { + for (SlashCommandKind v : values()) { + if (v.value.equals(value)) return v; + } + throw new IllegalArgumentException("Unknown SlashCommandKind value: " + value); + } +} diff --git a/src/main/java/com/github/copilot/sdk/CliServerManager.java b/src/main/java/com/github/copilot/sdk/CliServerManager.java index 3087966d39..bd4effe5a8 100644 --- a/src/main/java/com/github/copilot/sdk/CliServerManager.java +++ b/src/main/java/com/github/copilot/sdk/CliServerManager.java @@ -90,16 +90,16 @@ ProcessInfo startCliServer() throws IOException, InterruptedException { } // Default UseLoggedInUser to false when GitHubToken is provided - boolean useLoggedInUser = options.getUseLoggedInUser() != null - ? options.getUseLoggedInUser() - : (options.getGitHubToken() == null || options.getGitHubToken().isEmpty()); + boolean useLoggedInUser = options.getUseLoggedInUser() + .orElse(options.getGitHubToken() == null || options.getGitHubToken().isEmpty()); if (!useLoggedInUser) { args.add("--no-auto-login"); } - if (options.getSessionIdleTimeoutSeconds() != null && options.getSessionIdleTimeoutSeconds() > 0) { + if (options.getSessionIdleTimeoutSeconds().isPresent() + && options.getSessionIdleTimeoutSeconds().getAsInt() > 0) { args.add("--session-idle-timeout"); - args.add(String.valueOf(options.getSessionIdleTimeoutSeconds())); + args.add(String.valueOf(options.getSessionIdleTimeoutSeconds().getAsInt())); } if (options.isRemote()) { @@ -159,9 +159,9 @@ ProcessInfo startCliServer() throws IOException, InterruptedException { if (telemetry.getSourceName() != null) { pb.environment().put("COPILOT_OTEL_SOURCE_NAME", telemetry.getSourceName()); } - if (telemetry.getCaptureContent() != null) { + if (telemetry.getCaptureContent().isPresent()) { pb.environment().put("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", - telemetry.getCaptureContent() ? "true" : "false"); + telemetry.getCaptureContent().get() ? "true" : "false"); } } diff --git a/src/main/java/com/github/copilot/sdk/CopilotClient.java b/src/main/java/com/github/copilot/sdk/CopilotClient.java index 5b988d9d2b..4d0770319a 100644 --- a/src/main/java/com/github/copilot/sdk/CopilotClient.java +++ b/src/main/java/com/github/copilot/sdk/CopilotClient.java @@ -120,7 +120,7 @@ public CopilotClient(CopilotClientOptions options) { // Validate auth options with external server if (this.options.getCliUrl() != null && !this.options.getCliUrl().isEmpty() - && (this.options.getGitHubToken() != null || this.options.getUseLoggedInUser() != null)) { + && (this.options.getGitHubToken() != null || this.options.getUseLoggedInUser().isPresent())) { throw new IllegalArgumentException( "GitHubToken and UseLoggedInUser cannot be used with CliUrl (external server manages its own auth)"); } diff --git a/src/main/java/com/github/copilot/sdk/CopilotSession.java b/src/main/java/com/github/copilot/sdk/CopilotSession.java index ad5561c09e..79a7f343b7 100644 --- a/src/main/java/com/github/copilot/sdk/CopilotSession.java +++ b/src/main/java/com/github/copilot/sdk/CopilotSession.java @@ -1118,9 +1118,9 @@ private void handleElicitationRequestAsync(ElicitationContext context, String re */ private void assertElicitation() { SessionCapabilities caps = capabilities; - if (caps == null || caps.getUi() == null || !Boolean.TRUE.equals(caps.getUi().getElicitation())) { + if (caps == null || caps.getUi() == null || !caps.getUi().getElicitation().orElse(false)) { throw new IllegalStateException("Elicitation is not supported by the host. " - + "Check session.getCapabilities().getUi()?.getElicitation() before calling UI methods."); + + "Check session.getCapabilities().getUi().getElicitation().orElse(false) before calling UI methods."); } } @@ -1201,10 +1201,10 @@ public CompletableFuture input(String message, InputOptions options) { field.put("title", options.getTitle()); if (options.getDescription() != null) field.put("description", options.getDescription()); - if (options.getMinLength() != null) - field.put("minLength", options.getMinLength()); - if (options.getMaxLength() != null) - field.put("maxLength", options.getMaxLength()); + if (options.getMinLength().isPresent()) + field.put("minLength", options.getMinLength().getAsInt()); + if (options.getMaxLength().isPresent()) + field.put("maxLength", options.getMaxLength().getAsInt()); if (options.getFormat() != null) field.put("format", options.getFormat()); if (options.getDefaultValue() != null) @@ -1695,7 +1695,8 @@ public CompletableFuture setModel(String model, String reasoningEffort, ModelCapabilitiesOverrideSupports supports = null; if (modelCapabilities.getSupports() != null) { var s = modelCapabilities.getSupports(); - supports = new ModelCapabilitiesOverrideSupports(s.getVision(), s.getReasoningEffort()); + supports = new ModelCapabilitiesOverrideSupports(s.getVision().orElse(null), + s.getReasoningEffort().orElse(null)); } ModelCapabilitiesOverrideLimits limits = null; if (modelCapabilities.getLimits() != null) { diff --git a/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java b/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java index d9752db7ee..52bfb3337f 100644 --- a/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java +++ b/src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java @@ -111,12 +111,18 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setAvailableTools(config.getAvailableTools()); request.setExcludedTools(config.getExcludedTools()); request.setProvider(config.getProvider()); - request.setEnableSessionTelemetry(config.getEnableSessionTelemetry()); - request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null); - request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null); + config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry); + if (config.getOnUserInputRequest() != null) { + request.setRequestUserInput(true); + } + if (config.getHooks() != null && config.getHooks().hasHooks()) { + request.setHooks(true); + } request.setWorkingDirectory(config.getWorkingDirectory()); - request.setStreaming(config.isStreaming() ? true : null); - request.setIncludeSubAgentStreamingEvents(config.getIncludeSubAgentStreamingEvents()); + if (config.isStreaming()) { + request.setStreaming(true); + } + config.getIncludeSubAgentStreamingEvents().ifPresent(request::setIncludeSubAgentStreamingEvents); request.setMcpServers(config.getMcpServers()); request.setCustomAgents(config.getCustomAgents()); request.setDefaultAgent(config.getDefaultAgent()); @@ -126,7 +132,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setInstructionDirectories(config.getInstructionDirectories()); request.setDisabledSkills(config.getDisabledSkills()); request.setConfigDir(config.getConfigDir()); - request.setEnableConfigDiscovery(config.getEnableConfigDiscovery()); + config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery); request.setModelCapabilities(config.getModelCapabilities()); if (config.getCommands() != null && !config.getCommands().isEmpty()) { @@ -145,6 +151,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess request.setRequestAutoModeSwitch(true); } request.setGitHubToken(config.getGitHubToken()); + request.setRemoteSession(config.getRemoteSession()); return request; } @@ -194,15 +201,23 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setAvailableTools(config.getAvailableTools()); request.setExcludedTools(config.getExcludedTools()); request.setProvider(config.getProvider()); - request.setEnableSessionTelemetry(config.getEnableSessionTelemetry()); - request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null); - request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null); + config.getEnableSessionTelemetry().ifPresent(request::setEnableSessionTelemetry); + if (config.getOnUserInputRequest() != null) { + request.setRequestUserInput(true); + } + if (config.getHooks() != null && config.getHooks().hasHooks()) { + request.setHooks(true); + } request.setWorkingDirectory(config.getWorkingDirectory()); request.setConfigDir(config.getConfigDir()); - request.setEnableConfigDiscovery(config.getEnableConfigDiscovery()); - request.setDisableResume(config.isDisableResume() ? true : null); - request.setStreaming(config.isStreaming() ? true : null); - request.setIncludeSubAgentStreamingEvents(config.getIncludeSubAgentStreamingEvents()); + config.getEnableConfigDiscovery().ifPresent(request::setEnableConfigDiscovery); + if (config.isDisableResume()) { + request.setDisableResume(true); + } + if (config.isStreaming()) { + request.setStreaming(true); + } + config.getIncludeSubAgentStreamingEvents().ifPresent(request::setIncludeSubAgentStreamingEvents); request.setMcpServers(config.getMcpServers()); request.setCustomAgents(config.getCustomAgents()); request.setDefaultAgent(config.getDefaultAgent()); @@ -229,6 +244,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo request.setRequestAutoModeSwitch(true); } request.setGitHubToken(config.getGitHubToken()); + request.setRemoteSession(config.getRemoteSession()); return request; } diff --git a/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java b/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java index 5517df631e..e4605ffe10 100644 --- a/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java +++ b/src/main/java/com/github/copilot/sdk/json/CopilotClientOptions.java @@ -14,6 +14,9 @@ import java.util.function.Supplier; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.util.Optional; +import java.util.OptionalInt; /** * Configuration options for creating a @@ -499,34 +502,47 @@ public CopilotClientOptions setTelemetry(TelemetryConfig telemetry) { /** * Gets the server-wide idle timeout for sessions in seconds. * - * @return the session idle timeout in seconds, or {@code null} to disable - * (sessions live indefinitely) + * @return an {@link OptionalInt} containing the session idle timeout in + * seconds, or {@link java.util.OptionalInt#empty()} if not set. Use + * {@link #clearSessionIdleTimeoutSeconds()} to revert to the default. * @since 1.3.0 */ - public Integer getSessionIdleTimeoutSeconds() { - return sessionIdleTimeoutSeconds; + @JsonIgnore + public OptionalInt getSessionIdleTimeoutSeconds() { + return sessionIdleTimeoutSeconds == null ? OptionalInt.empty() : OptionalInt.of(sessionIdleTimeoutSeconds); } /** * Sets the server-wide idle timeout for sessions in seconds. *

* Sessions without activity for this duration are automatically cleaned up. Set - * to {@code 0} or leave as {@code null} to disable (sessions live - * indefinitely). + * to {@code 0} to disable (sessions live indefinitely). Use + * {@link #clearSessionIdleTimeoutSeconds()} to revert to the default. *

* This option is only used when the SDK spawns the CLI process; it is ignored * when connecting to an external server via {@link #setCliUrl(String)}. * * @param sessionIdleTimeoutSeconds - * the idle timeout in seconds, or {@code null} to disable + * the idle timeout in seconds * @return this options instance for method chaining * @since 1.3.0 */ - public CopilotClientOptions setSessionIdleTimeoutSeconds(Integer sessionIdleTimeoutSeconds) { + public CopilotClientOptions setSessionIdleTimeoutSeconds(int sessionIdleTimeoutSeconds) { this.sessionIdleTimeoutSeconds = sessionIdleTimeoutSeconds; return this; } + /** + * Clears the sessionIdleTimeoutSeconds setting, reverting to the default + * behavior. + * + * @return this instance for method chaining + */ + public CopilotClientOptions clearSessionIdleTimeoutSeconds() { + this.sessionIdleTimeoutSeconds = null; + return this; + } + /** * Gets the connection token for the headless CLI server (TCP only). * @@ -555,11 +571,11 @@ public CopilotClientOptions setTcpConnectionToken(String tcpConnectionToken) { /** * Returns whether to use the logged-in user for authentication. * - * @return {@code true} to use logged-in user auth, {@code false} to use only - * explicit tokens, or {@code null} to use default behavior + * @return an {@link Optional} containing the boolean value, or empty if not set */ - public Boolean getUseLoggedInUser() { - return useLoggedInUser; + @JsonIgnore + public Optional getUseLoggedInUser() { + return Optional.ofNullable(useLoggedInUser); } /** @@ -569,15 +585,23 @@ public Boolean getUseLoggedInUser() { * auth. When false, only explicit tokens (gitHubToken or environment variables) * are used. Default: true (but defaults to false when gitHubToken is provided). *

- * Passing {@code null} is equivalent to passing {@link Boolean#FALSE}. * * @param useLoggedInUser - * {@code true} to use logged-in user auth, {@code false} or - * {@code null} otherwise + * {@code true} to use logged-in user auth, {@code false} otherwise * @return this options instance for method chaining */ - public CopilotClientOptions setUseLoggedInUser(Boolean useLoggedInUser) { - this.useLoggedInUser = useLoggedInUser != null ? useLoggedInUser : Boolean.FALSE; + public CopilotClientOptions setUseLoggedInUser(boolean useLoggedInUser) { + this.useLoggedInUser = useLoggedInUser; + return this; + } + + /** + * Clears the useLoggedInUser setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public CopilotClientOptions clearUseLoggedInUser() { + this.useLoggedInUser = null; return this; } diff --git a/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java b/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java index 12bab4154c..d6bcc7b2b7 100644 --- a/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java +++ b/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java @@ -124,6 +124,9 @@ public final class CreateSessionRequest { @JsonProperty("gitHubToken") private String gitHubToken; + @JsonProperty("remoteSession") + private String remoteSession; + /** Gets the model name. @return the model */ public String getModel() { return model; @@ -224,40 +227,68 @@ public Boolean getEnableSessionTelemetry() { /** * Sets enable session telemetry flag. @param enableSessionTelemetry the flag */ - public void setEnableSessionTelemetry(Boolean enableSessionTelemetry) { + public void setEnableSessionTelemetry(boolean enableSessionTelemetry) { this.enableSessionTelemetry = enableSessionTelemetry; } + /** + * Clears the enableSessionTelemetry setting, reverting to the default behavior. + */ + public void clearEnableSessionTelemetry() { + this.enableSessionTelemetry = null; + } + /** Gets request permission flag. @return the flag */ public Boolean getRequestPermission() { return requestPermission; } /** Sets request permission flag. @param requestPermission the flag */ - public void setRequestPermission(Boolean requestPermission) { + public void setRequestPermission(boolean requestPermission) { this.requestPermission = requestPermission; } + /** + * Clears the requestPermission setting, reverting to the default behavior. + */ + public void clearRequestPermission() { + this.requestPermission = null; + } + /** Gets request user input flag. @return the flag */ public Boolean getRequestUserInput() { return requestUserInput; } /** Sets request user input flag. @param requestUserInput the flag */ - public void setRequestUserInput(Boolean requestUserInput) { + public void setRequestUserInput(boolean requestUserInput) { this.requestUserInput = requestUserInput; } + /** + * Clears the requestUserInput setting, reverting to the default behavior. + */ + public void clearRequestUserInput() { + this.requestUserInput = null; + } + /** Gets hooks flag. @return the flag */ public Boolean getHooks() { return hooks; } /** Sets hooks flag. @param hooks the flag */ - public void setHooks(Boolean hooks) { + public void setHooks(boolean hooks) { this.hooks = hooks; } + /** + * Clears the hooks setting, reverting to the default behavior. + */ + public void clearHooks() { + this.hooks = null; + } + /** Gets working directory. @return the working directory */ public String getWorkingDirectory() { return workingDirectory; @@ -274,10 +305,17 @@ public Boolean getStreaming() { } /** Sets streaming flag. @param streaming the flag */ - public void setStreaming(Boolean streaming) { + public void setStreaming(boolean streaming) { this.streaming = streaming; } + /** + * Clears the streaming setting, reverting to the default behavior. + */ + public void clearStreaming() { + this.streaming = null; + } + /** Gets MCP servers. @return the servers map */ public Map getMcpServers() { return mcpServers == null ? null : Collections.unmodifiableMap(mcpServers); @@ -388,10 +426,17 @@ public Boolean getEnableConfigDiscovery() { } /** Sets enable config discovery flag. @param enableConfigDiscovery the flag */ - public void setEnableConfigDiscovery(Boolean enableConfigDiscovery) { + public void setEnableConfigDiscovery(boolean enableConfigDiscovery) { this.enableConfigDiscovery = enableConfigDiscovery; } + /** + * Clears the enableConfigDiscovery setting, reverting to the default behavior. + */ + public void clearEnableConfigDiscovery() { + this.enableConfigDiscovery = null; + } + /** Gets include sub-agent streaming events flag. @return the flag */ public Boolean getIncludeSubAgentStreamingEvents() { return includeSubAgentStreamingEvents; @@ -401,10 +446,18 @@ public Boolean getIncludeSubAgentStreamingEvents() { * Sets include sub-agent streaming events flag. @param * includeSubAgentStreamingEvents the flag */ - public void setIncludeSubAgentStreamingEvents(Boolean includeSubAgentStreamingEvents) { + public void setIncludeSubAgentStreamingEvents(boolean includeSubAgentStreamingEvents) { this.includeSubAgentStreamingEvents = includeSubAgentStreamingEvents; } + /** + * Clears the includeSubAgentStreamingEvents setting, reverting to the default + * behavior. + */ + public void clearIncludeSubAgentStreamingEvents() { + this.includeSubAgentStreamingEvents = null; + } + /** Gets the commands wire definitions. @return the commands */ public List getCommands() { return commands == null ? null : Collections.unmodifiableList(commands); @@ -421,10 +474,17 @@ public Boolean getRequestElicitation() { } /** Sets the requestElicitation flag. @param requestElicitation the flag */ - public void setRequestElicitation(Boolean requestElicitation) { + public void setRequestElicitation(boolean requestElicitation) { this.requestElicitation = requestElicitation; } + /** + * Clears the requestElicitation setting, reverting to the default behavior. + */ + public void clearRequestElicitation() { + this.requestElicitation = null; + } + /** Gets the requestExitPlanMode flag. @return the flag */ public Boolean getRequestExitPlanMode() { return requestExitPlanMode; @@ -471,4 +531,16 @@ public String getGitHubToken() { public void setGitHubToken(String gitHubToken) { this.gitHubToken = gitHubToken; } + + /** Gets the remote session mode. @return the remote session mode */ + public String getRemoteSession() { + return remoteSession; + } + + /** + * Sets the remote session mode. @param remoteSession the remote session mode + */ + public void setRemoteSession(String remoteSession) { + this.remoteSession = remoteSession; + } } diff --git a/src/main/java/com/github/copilot/sdk/json/CustomAgentConfig.java b/src/main/java/com/github/copilot/sdk/json/CustomAgentConfig.java index 1421db603f..bb9520055e 100644 --- a/src/main/java/com/github/copilot/sdk/json/CustomAgentConfig.java +++ b/src/main/java/com/github/copilot/sdk/json/CustomAgentConfig.java @@ -10,6 +10,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.util.Optional; /** * Configuration for a custom agent in a Copilot session. @@ -197,10 +199,12 @@ public CustomAgentConfig setMcpServers(Map mcpServers) /** * Gets whether inference mode is enabled. * - * @return the infer flag, or {@code null} if not set + * @return an {@link java.util.Optional} containing the infer flag, or + * {@link java.util.Optional#empty()} if not set */ - public Boolean getInfer() { - return infer; + @JsonIgnore + public Optional getInfer() { + return Optional.ofNullable(infer); } /** @@ -210,11 +214,21 @@ public Boolean getInfer() { * {@code true} to enable inference mode * @return this config for method chaining */ - public CustomAgentConfig setInfer(Boolean infer) { + public CustomAgentConfig setInfer(boolean infer) { this.infer = infer; return this; } + /** + * Clears the infer setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public CustomAgentConfig clearInfer() { + this.infer = null; + return this; + } + /** * Gets the list of skill names to preload into this agent's context. * diff --git a/src/main/java/com/github/copilot/sdk/json/InfiniteSessionConfig.java b/src/main/java/com/github/copilot/sdk/json/InfiniteSessionConfig.java index d45851f8a1..561796ede7 100644 --- a/src/main/java/com/github/copilot/sdk/json/InfiniteSessionConfig.java +++ b/src/main/java/com/github/copilot/sdk/json/InfiniteSessionConfig.java @@ -6,6 +6,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.util.Optional; +import java.util.OptionalDouble; /** * Configuration for infinite sessions with automatic context compaction and @@ -43,10 +46,12 @@ public class InfiniteSessionConfig { /** * Gets whether infinite sessions are enabled. * - * @return {@code true} if enabled, {@code null} to use default (true) + * @return an {@link Optional} containing the boolean value, or empty to use + * default (true) */ - public Boolean getEnabled() { - return enabled; + @JsonIgnore + public Optional getEnabled() { + return Optional.ofNullable(enabled); } /** @@ -58,18 +63,32 @@ public Boolean getEnabled() { * {@code true} to enable infinite sessions * @return this config instance for method chaining */ - public InfiniteSessionConfig setEnabled(Boolean enabled) { + public InfiniteSessionConfig setEnabled(boolean enabled) { this.enabled = enabled; return this; } + /** + * Clears the enabled setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public InfiniteSessionConfig clearEnabled() { + this.enabled = null; + return this; + } + /** * Gets the background compaction threshold. * - * @return the threshold (0.0-1.0), or {@code null} to use default + * @return an {@link OptionalDouble} containing the threshold (0.0-1.0), or + * empty to use default */ - public Double getBackgroundCompactionThreshold() { - return backgroundCompactionThreshold; + @JsonIgnore + public OptionalDouble getBackgroundCompactionThreshold() { + return backgroundCompactionThreshold == null + ? OptionalDouble.empty() + : OptionalDouble.of(backgroundCompactionThreshold); } /** @@ -82,18 +101,33 @@ public Double getBackgroundCompactionThreshold() { * the threshold (0.0-1.0) * @return this config instance for method chaining */ - public InfiniteSessionConfig setBackgroundCompactionThreshold(Double backgroundCompactionThreshold) { + public InfiniteSessionConfig setBackgroundCompactionThreshold(double backgroundCompactionThreshold) { this.backgroundCompactionThreshold = backgroundCompactionThreshold; return this; } + /** + * Clears the backgroundCompactionThreshold setting, reverting to the default + * behavior. + * + * @return this instance for method chaining + */ + public InfiniteSessionConfig clearBackgroundCompactionThreshold() { + this.backgroundCompactionThreshold = null; + return this; + } + /** * Gets the buffer exhaustion threshold. * - * @return the threshold (0.0-1.0), or {@code null} to use default + * @return an {@link OptionalDouble} containing the threshold (0.0-1.0), or + * empty to use default */ - public Double getBufferExhaustionThreshold() { - return bufferExhaustionThreshold; + @JsonIgnore + public OptionalDouble getBufferExhaustionThreshold() { + return bufferExhaustionThreshold == null + ? OptionalDouble.empty() + : OptionalDouble.of(bufferExhaustionThreshold); } /** @@ -107,8 +141,20 @@ public Double getBufferExhaustionThreshold() { * the threshold (0.0-1.0) * @return this config instance for method chaining */ - public InfiniteSessionConfig setBufferExhaustionThreshold(Double bufferExhaustionThreshold) { + public InfiniteSessionConfig setBufferExhaustionThreshold(double bufferExhaustionThreshold) { this.bufferExhaustionThreshold = bufferExhaustionThreshold; return this; } + + /** + * Clears the bufferExhaustionThreshold setting, reverting to the default + * behavior. + * + * @return this instance for method chaining + */ + public InfiniteSessionConfig clearBufferExhaustionThreshold() { + this.bufferExhaustionThreshold = null; + return this; + } + } diff --git a/src/main/java/com/github/copilot/sdk/json/InputOptions.java b/src/main/java/com/github/copilot/sdk/json/InputOptions.java index 9b0b6c8dd8..ca1cc5d385 100644 --- a/src/main/java/com/github/copilot/sdk/json/InputOptions.java +++ b/src/main/java/com/github/copilot/sdk/json/InputOptions.java @@ -4,17 +4,25 @@ package com.github.copilot.sdk.json; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.OptionalInt; + /** * Options for the {@link SessionUiApi#input(String, InputOptions)} convenience * method. * * @since 1.0.0 */ +@JsonInclude(JsonInclude.Include.NON_NULL) public class InputOptions { private String title; private String description; + @JsonProperty("minLength") private Integer minLength; + @JsonProperty("maxLength") private Integer maxLength; private String format; private String defaultValue; @@ -46,34 +54,66 @@ public InputOptions setDescription(String description) { return this; } - /** Gets the minimum character length. @return the min length */ - public Integer getMinLength() { - return minLength; + /** + * Gets the minimum character length. + * + * @return an {@link java.util.OptionalInt} containing the min length, or + * {@link java.util.OptionalInt#empty()} if not set + */ + @JsonIgnore + public OptionalInt getMinLength() { + return minLength == null ? OptionalInt.empty() : OptionalInt.of(minLength); } /** * Sets the minimum character length. @param minLength the min length @return * this */ - public InputOptions setMinLength(Integer minLength) { + public InputOptions setMinLength(int minLength) { this.minLength = minLength; return this; } - /** Gets the maximum character length. @return the max length */ - public Integer getMaxLength() { - return maxLength; + /** + * Clears the minLength setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public InputOptions clearMinLength() { + this.minLength = null; + return this; + } + + /** + * Gets the maximum character length. + * + * @return an {@link java.util.OptionalInt} containing the max length, or + * {@link java.util.OptionalInt#empty()} if not set + */ + @JsonIgnore + public OptionalInt getMaxLength() { + return maxLength == null ? OptionalInt.empty() : OptionalInt.of(maxLength); } /** * Sets the maximum character length. @param maxLength the max length @return * this */ - public InputOptions setMaxLength(Integer maxLength) { + public InputOptions setMaxLength(int maxLength) { this.maxLength = maxLength; return this; } + /** + * Clears the maxLength setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public InputOptions clearMaxLength() { + this.maxLength = null; + return this; + } + /** * Gets the semantic format hint (e.g., {@code "email"}, {@code "uri"}, * {@code "date"}, {@code "date-time"}). diff --git a/src/main/java/com/github/copilot/sdk/json/ModelCapabilitiesOverride.java b/src/main/java/com/github/copilot/sdk/json/ModelCapabilitiesOverride.java index 18701ad671..02727d4b5c 100644 --- a/src/main/java/com/github/copilot/sdk/json/ModelCapabilitiesOverride.java +++ b/src/main/java/com/github/copilot/sdk/json/ModelCapabilitiesOverride.java @@ -7,6 +7,9 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.util.Optional; +import java.util.OptionalInt; /** * Per-property overrides for model capabilities, deep-merged over runtime @@ -106,48 +109,73 @@ public static class Supports { /** * Gets the vision override. * - * @return {@code true} to enable vision, {@code false} to disable, or - * {@code null} to use the runtime default + * @return an {@link java.util.Optional} containing {@code true} to enable + * vision or {@code false} to disable, or + * {@link java.util.Optional#empty()} to use the runtime default */ - public Boolean getVision() { - return vision; + @JsonIgnore + public Optional getVision() { + return Optional.ofNullable(vision); } /** - * Sets whether vision (image input) is enabled. + * Sets whether vision (image input) is enabled. Use {@link #clearVision()} to + * revert to the runtime default. * * @param vision - * {@code true} to enable, {@code false} to disable, or {@code null} - * to use the runtime default + * {@code true} to enable, {@code false} to disable * @return this instance for method chaining */ - public Supports setVision(Boolean vision) { + public Supports setVision(boolean vision) { this.vision = vision; return this; } + /** + * Clears the vision setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public Supports clearVision() { + this.vision = null; + return this; + } + /** * Gets the reasoning effort override. * - * @return {@code true} to enable reasoning effort, {@code false} to disable, or - * {@code null} to use the runtime default + * @return an {@link java.util.Optional} containing {@code true} to enable + * reasoning effort or {@code false} to disable, or + * {@link java.util.Optional#empty()} to use the runtime default */ - public Boolean getReasoningEffort() { - return reasoningEffort; + @JsonIgnore + public Optional getReasoningEffort() { + return Optional.ofNullable(reasoningEffort); } /** - * Sets whether reasoning effort configuration is enabled. + * Sets whether reasoning effort configuration is enabled. Use + * {@link #clearReasoningEffort()} to revert to the runtime default. * * @param reasoningEffort - * {@code true} to enable, {@code false} to disable, or {@code null} - * to use the runtime default + * {@code true} to enable, {@code false} to disable * @return this instance for method chaining */ - public Supports setReasoningEffort(Boolean reasoningEffort) { + public Supports setReasoningEffort(boolean reasoningEffort) { this.reasoningEffort = reasoningEffort; return this; } + + /** + * Clears the reasoningEffort setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public Supports clearReasoningEffort() { + this.reasoningEffort = null; + return this; + } + } /** @@ -174,8 +202,9 @@ public static class Limits { * * @return the override value, or {@code null} to use the runtime default */ - public Integer getMaxPromptTokens() { - return maxPromptTokens; + @JsonIgnore + public OptionalInt getMaxPromptTokens() { + return maxPromptTokens == null ? OptionalInt.empty() : OptionalInt.of(maxPromptTokens); } /** @@ -185,18 +214,29 @@ public Integer getMaxPromptTokens() { * the override value, or {@code null} to use the runtime default * @return this instance for method chaining */ - public Limits setMaxPromptTokens(Integer maxPromptTokens) { + public Limits setMaxPromptTokens(int maxPromptTokens) { this.maxPromptTokens = maxPromptTokens; return this; } + /** + * Clears the maxPromptTokens setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public Limits clearMaxPromptTokens() { + this.maxPromptTokens = null; + return this; + } + /** * Gets the maximum output tokens override. * * @return the override value, or {@code null} to use the runtime default */ - public Integer getMaxOutputTokens() { - return maxOutputTokens; + @JsonIgnore + public OptionalInt getMaxOutputTokens() { + return maxOutputTokens == null ? OptionalInt.empty() : OptionalInt.of(maxOutputTokens); } /** @@ -206,18 +246,29 @@ public Integer getMaxOutputTokens() { * the override value, or {@code null} to use the runtime default * @return this instance for method chaining */ - public Limits setMaxOutputTokens(Integer maxOutputTokens) { + public Limits setMaxOutputTokens(int maxOutputTokens) { this.maxOutputTokens = maxOutputTokens; return this; } + /** + * Clears the maxOutputTokens setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public Limits clearMaxOutputTokens() { + this.maxOutputTokens = null; + return this; + } + /** * Gets the maximum context window tokens override. * * @return the override value, or {@code null} to use the runtime default */ - public Integer getMaxContextWindowTokens() { - return maxContextWindowTokens; + @JsonIgnore + public OptionalInt getMaxContextWindowTokens() { + return maxContextWindowTokens == null ? OptionalInt.empty() : OptionalInt.of(maxContextWindowTokens); } /** @@ -227,9 +278,20 @@ public Integer getMaxContextWindowTokens() { * the override value, or {@code null} to use the runtime default * @return this instance for method chaining */ - public Limits setMaxContextWindowTokens(Integer maxContextWindowTokens) { + public Limits setMaxContextWindowTokens(int maxContextWindowTokens) { this.maxContextWindowTokens = maxContextWindowTokens; return this; } + + /** + * Clears the maxContextWindowTokens setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public Limits clearMaxContextWindowTokens() { + this.maxContextWindowTokens = null; + return this; + } + } } diff --git a/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java b/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java index 8947696c9a..1c5e6fcc7f 100644 --- a/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java +++ b/src/main/java/com/github/copilot/sdk/json/ProviderConfig.java @@ -9,6 +9,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.util.OptionalInt; /** * Configuration for a custom API provider (BYOK - Bring Your Own Key). @@ -296,10 +298,12 @@ public ProviderConfig setWireModel(String wireModel) { /** * Gets the maximum prompt token override. * - * @return the max prompt tokens, or {@code null} if not set + * @return an {@link java.util.OptionalInt} containing the max prompt tokens, or + * {@link java.util.OptionalInt#empty()} if not set */ - public Integer getMaxPromptTokens() { - return maxPromptTokens; + @JsonIgnore + public OptionalInt getMaxPromptTokens() { + return maxPromptTokens == null ? OptionalInt.empty() : OptionalInt.of(maxPromptTokens); } /** @@ -314,18 +318,30 @@ public Integer getMaxPromptTokens() { * the max prompt tokens * @return this config for method chaining */ - public ProviderConfig setMaxPromptTokens(Integer maxPromptTokens) { + public ProviderConfig setMaxPromptTokens(int maxPromptTokens) { this.maxPromptTokens = maxPromptTokens; return this; } + /** + * Clears the maxPromptTokens setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ProviderConfig clearMaxPromptTokens() { + this.maxPromptTokens = null; + return this; + } + /** * Gets the maximum output token override. * - * @return the max output tokens, or {@code null} if not set + * @return an {@link java.util.OptionalInt} containing the max output tokens, or + * {@link java.util.OptionalInt#empty()} if not set */ - public Integer getMaxOutputTokens() { - return maxOutputTokens; + @JsonIgnore + public OptionalInt getMaxOutputTokens() { + return maxOutputTokens == null ? OptionalInt.empty() : OptionalInt.of(maxOutputTokens); } /** @@ -338,8 +354,19 @@ public Integer getMaxOutputTokens() { * the max output tokens * @return this config for method chaining */ - public ProviderConfig setMaxOutputTokens(Integer maxOutputTokens) { + public ProviderConfig setMaxOutputTokens(int maxOutputTokens) { this.maxOutputTokens = maxOutputTokens; return this; } + + /** + * Clears the maxOutputTokens setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ProviderConfig clearMaxOutputTokens() { + this.maxOutputTokens = null; + return this; + } + } diff --git a/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java b/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java index f2caad7710..72c9f6f47a 100644 --- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java +++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java @@ -11,8 +11,10 @@ import java.util.function.Consumer; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.github.copilot.sdk.generated.SessionEvent; +import java.util.Optional; /** * Configuration for resuming an existing Copilot session. @@ -69,6 +71,7 @@ public class ResumeSessionConfig { private ExitPlanModeHandler onExitPlanMode; private AutoModeSwitchHandler onAutoModeSwitch; private String gitHubToken; + private String remoteSession; /** * Gets the AI model to use. @@ -234,7 +237,7 @@ public ResumeSessionConfig setProvider(ProviderConfig provider) { /** * Enables or disables internal session telemetry for this session. When - * {@code false}, disables session telemetry. When {@code null} (the default) or + * {@code false}, disables session telemetry. When unset (the default) or * {@code true}, telemetry is enabled for GitHub-authenticated sessions. When a * custom {@link ProviderConfig} (BYOK) is configured, session telemetry is * always disabled regardless of this setting. This is independent of @@ -242,15 +245,17 @@ public ResumeSessionConfig setProvider(ProviderConfig provider) { * CopilotClientOptions.TelemetryConfig}, which configures OpenTelemetry export * for observability. * - * @return whether session telemetry is enabled + * @return an {@link java.util.Optional} containing whether session telemetry is + * enabled, or {@link java.util.Optional#empty()} for the default */ - public Boolean getEnableSessionTelemetry() { - return enableSessionTelemetry; + @JsonIgnore + public Optional getEnableSessionTelemetry() { + return Optional.ofNullable(enableSessionTelemetry); } /** * Enables or disables internal session telemetry for this session. When - * {@code false}, disables session telemetry. When {@code null} (the default) or + * {@code false}, disables session telemetry. When unset (the default) or * {@code true}, telemetry is enabled for GitHub-authenticated sessions. When a * custom {@link ProviderConfig} (BYOK) is configured, session telemetry is * always disabled regardless of this setting. @@ -259,11 +264,21 @@ public Boolean getEnableSessionTelemetry() { * whether to enable session telemetry * @return this config for method chaining */ - public ResumeSessionConfig setEnableSessionTelemetry(Boolean enableSessionTelemetry) { + public ResumeSessionConfig setEnableSessionTelemetry(boolean enableSessionTelemetry) { this.enableSessionTelemetry = enableSessionTelemetry; return this; } + /** + * Clears the enableSessionTelemetry setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableSessionTelemetry() { + this.enableSessionTelemetry = null; + return this; + } + /** * Gets the reasoning effort level. * @@ -403,8 +418,9 @@ public ResumeSessionConfig setConfigDir(String configDir) { * @return {@code true} to enable discovery, {@code false} to disable, or * {@code null} to use the runtime default */ - public Boolean getEnableConfigDiscovery() { - return enableConfigDiscovery; + @JsonIgnore + public Optional getEnableConfigDiscovery() { + return Optional.ofNullable(enableConfigDiscovery); } /** @@ -420,19 +436,30 @@ public Boolean getEnableConfigDiscovery() { * {@code null} to use the runtime default * @return this config for method chaining */ - public ResumeSessionConfig setEnableConfigDiscovery(Boolean enableConfigDiscovery) { + public ResumeSessionConfig setEnableConfigDiscovery(boolean enableConfigDiscovery) { this.enableConfigDiscovery = enableConfigDiscovery; return this; } + /** + * Clears the enableConfigDiscovery setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearEnableConfigDiscovery() { + this.enableConfigDiscovery = null; + return this; + } + /** * Gets whether sub-agent streaming events are included. * * @return {@code true} to include sub-agent streaming events, {@code false} to * suppress them, or {@code null} to use the runtime default */ - public Boolean getIncludeSubAgentStreamingEvents() { - return includeSubAgentStreamingEvents; + @JsonIgnore + public Optional getIncludeSubAgentStreamingEvents() { + return Optional.ofNullable(includeSubAgentStreamingEvents); } /** @@ -443,11 +470,22 @@ public Boolean getIncludeSubAgentStreamingEvents() { * suppress * @return this config for method chaining */ - public ResumeSessionConfig setIncludeSubAgentStreamingEvents(Boolean includeSubAgentStreamingEvents) { + public ResumeSessionConfig setIncludeSubAgentStreamingEvents(boolean includeSubAgentStreamingEvents) { this.includeSubAgentStreamingEvents = includeSubAgentStreamingEvents; return this; } + /** + * Clears the includeSubAgentStreamingEvents setting, reverting to the default + * behavior. + * + * @return this instance for method chaining + */ + public ResumeSessionConfig clearIncludeSubAgentStreamingEvents() { + this.includeSubAgentStreamingEvents = null; + return this; + } + /** * Gets the model capabilities override. * @@ -849,6 +887,34 @@ public ResumeSessionConfig setGitHubToken(String gitHubToken) { return this; } + /** + * Gets the per-session remote behavior control. + *

+ * See {@link SessionConfig#getRemoteSession()} for details on possible values. + * + * @return the remote session mode, or {@code null} if not set + * @since 1.4.0 + */ + public String getRemoteSession() { + return remoteSession; + } + + /** + * Sets the per-session remote behavior control. + *

+ * See {@link SessionConfig#setRemoteSession(String)} for details on possible + * values. + * + * @param remoteSession + * the remote session mode + * @return this config for method chaining + * @since 1.4.0 + */ + public ResumeSessionConfig setRemoteSession(String remoteSession) { + this.remoteSession = remoteSession; + return this; + } + /** * Creates a shallow clone of this {@code ResumeSessionConfig} instance. *

@@ -898,6 +964,7 @@ public ResumeSessionConfig clone() { copy.onExitPlanMode = this.onExitPlanMode; copy.onAutoModeSwitch = this.onAutoModeSwitch; copy.gitHubToken = this.gitHubToken; + copy.remoteSession = this.remoteSession; return copy; } } diff --git a/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java b/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java index a1af269700..8aca77b7d2 100644 --- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java +++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java @@ -128,6 +128,9 @@ public final class ResumeSessionRequest { @JsonProperty("gitHubToken") private String gitHubToken; + @JsonProperty("remoteSession") + private String remoteSession; + /** Gets the session ID. @return the session ID */ public String getSessionId() { return sessionId; @@ -231,40 +234,68 @@ public Boolean getEnableSessionTelemetry() { /** * Sets enable session telemetry flag. @param enableSessionTelemetry the flag */ - public void setEnableSessionTelemetry(Boolean enableSessionTelemetry) { + public void setEnableSessionTelemetry(boolean enableSessionTelemetry) { this.enableSessionTelemetry = enableSessionTelemetry; } + /** + * Clears the enableSessionTelemetry setting, reverting to the default behavior. + */ + public void clearEnableSessionTelemetry() { + this.enableSessionTelemetry = null; + } + /** Gets request permission flag. @return the flag */ public Boolean getRequestPermission() { return requestPermission; } /** Sets request permission flag. @param requestPermission the flag */ - public void setRequestPermission(Boolean requestPermission) { + public void setRequestPermission(boolean requestPermission) { this.requestPermission = requestPermission; } + /** + * Clears the requestPermission setting, reverting to the default behavior. + */ + public void clearRequestPermission() { + this.requestPermission = null; + } + /** Gets request user input flag. @return the flag */ public Boolean getRequestUserInput() { return requestUserInput; } /** Sets request user input flag. @param requestUserInput the flag */ - public void setRequestUserInput(Boolean requestUserInput) { + public void setRequestUserInput(boolean requestUserInput) { this.requestUserInput = requestUserInput; } + /** + * Clears the requestUserInput setting, reverting to the default behavior. + */ + public void clearRequestUserInput() { + this.requestUserInput = null; + } + /** Gets hooks flag. @return the flag */ public Boolean getHooks() { return hooks; } /** Sets hooks flag. @param hooks the flag */ - public void setHooks(Boolean hooks) { + public void setHooks(boolean hooks) { this.hooks = hooks; } + /** + * Clears the hooks setting, reverting to the default behavior. + */ + public void clearHooks() { + this.hooks = null; + } + /** Gets working directory. @return the working directory */ public String getWorkingDirectory() { return workingDirectory; @@ -291,30 +322,51 @@ public Boolean getEnableConfigDiscovery() { } /** Sets enable config discovery flag. @param enableConfigDiscovery the flag */ - public void setEnableConfigDiscovery(Boolean enableConfigDiscovery) { + public void setEnableConfigDiscovery(boolean enableConfigDiscovery) { this.enableConfigDiscovery = enableConfigDiscovery; } + /** + * Clears the enableConfigDiscovery setting, reverting to the default behavior. + */ + public void clearEnableConfigDiscovery() { + this.enableConfigDiscovery = null; + } + /** Gets disable resume flag. @return the flag */ public Boolean getDisableResume() { return disableResume; } /** Sets disable resume flag. @param disableResume the flag */ - public void setDisableResume(Boolean disableResume) { + public void setDisableResume(boolean disableResume) { this.disableResume = disableResume; } + /** + * Clears the disableResume setting, reverting to the default behavior. + */ + public void clearDisableResume() { + this.disableResume = null; + } + /** Gets streaming flag. @return the flag */ public Boolean getStreaming() { return streaming; } /** Sets streaming flag. @param streaming the flag */ - public void setStreaming(Boolean streaming) { + public void setStreaming(boolean streaming) { this.streaming = streaming; } + /** + * Clears the streaming setting, reverting to the default behavior. + */ + public void clearStreaming() { + this.streaming = null; + } + /** Gets include sub-agent streaming events flag. @return the flag */ public Boolean getIncludeSubAgentStreamingEvents() { return includeSubAgentStreamingEvents; @@ -324,10 +376,18 @@ public Boolean getIncludeSubAgentStreamingEvents() { * Sets include sub-agent streaming events flag. @param * includeSubAgentStreamingEvents the flag */ - public void setIncludeSubAgentStreamingEvents(Boolean includeSubAgentStreamingEvents) { + public void setIncludeSubAgentStreamingEvents(boolean includeSubAgentStreamingEvents) { this.includeSubAgentStreamingEvents = includeSubAgentStreamingEvents; } + /** + * Clears the includeSubAgentStreamingEvents setting, reverting to the default + * behavior. + */ + public void clearIncludeSubAgentStreamingEvents() { + this.includeSubAgentStreamingEvents = null; + } + /** Gets MCP servers. @return the servers map */ public Map getMcpServers() { return mcpServers == null ? null : Collections.unmodifiableMap(mcpServers); @@ -441,10 +501,17 @@ public Boolean getRequestElicitation() { } /** Sets the requestElicitation flag. @param requestElicitation the flag */ - public void setRequestElicitation(Boolean requestElicitation) { + public void setRequestElicitation(boolean requestElicitation) { this.requestElicitation = requestElicitation; } + /** + * Clears the requestElicitation setting, reverting to the default behavior. + */ + public void clearRequestElicitation() { + this.requestElicitation = null; + } + /** Gets the requestExitPlanMode flag. @return the flag */ public Boolean getRequestExitPlanMode() { return requestExitPlanMode; @@ -491,4 +558,16 @@ public String getGitHubToken() { public void setGitHubToken(String gitHubToken) { this.gitHubToken = gitHubToken; } + + /** Gets the remote session mode. @return the remote session mode */ + public String getRemoteSession() { + return remoteSession; + } + + /** + * Sets the remote session mode. @param remoteSession the remote session mode + */ + public void setRemoteSession(String remoteSession) { + this.remoteSession = remoteSession; + } } diff --git a/src/main/java/com/github/copilot/sdk/json/SessionConfig.java b/src/main/java/com/github/copilot/sdk/json/SessionConfig.java index 51cad884ed..f1a383402f 100644 --- a/src/main/java/com/github/copilot/sdk/json/SessionConfig.java +++ b/src/main/java/com/github/copilot/sdk/json/SessionConfig.java @@ -11,8 +11,10 @@ import java.util.function.Consumer; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.github.copilot.sdk.generated.SessionEvent; +import java.util.Optional; /** * Configuration for creating a new Copilot session. @@ -69,6 +71,7 @@ public class SessionConfig { private ExitPlanModeHandler onExitPlanMode; private AutoModeSwitchHandler onAutoModeSwitch; private String gitHubToken; + private String remoteSession; /** * Gets the custom session ID. @@ -288,7 +291,7 @@ public SessionConfig setProvider(ProviderConfig provider) { /** * Enables or disables internal session telemetry for this session. When - * {@code false}, disables session telemetry. When {@code null} (the default) or + * {@code false}, disables session telemetry. When unset (the default) or * {@code true}, telemetry is enabled for GitHub-authenticated sessions. When a * custom {@link ProviderConfig} (BYOK) is configured, session telemetry is * always disabled regardless of this setting. This is independent of @@ -296,15 +299,17 @@ public SessionConfig setProvider(ProviderConfig provider) { * CopilotClientOptions.TelemetryConfig}, which configures OpenTelemetry export * for observability. * - * @return whether session telemetry is enabled + * @return an {@link java.util.Optional} containing whether session telemetry is + * enabled, or {@link java.util.Optional#empty()} for the default */ - public Boolean getEnableSessionTelemetry() { - return enableSessionTelemetry; + @JsonIgnore + public Optional getEnableSessionTelemetry() { + return Optional.ofNullable(enableSessionTelemetry); } /** * Enables or disables internal session telemetry for this session. When - * {@code false}, disables session telemetry. When {@code null} (the default) or + * {@code false}, disables session telemetry. When unset (the default) or * {@code true}, telemetry is enabled for GitHub-authenticated sessions. When a * custom {@link ProviderConfig} (BYOK) is configured, session telemetry is * always disabled regardless of this setting. @@ -313,11 +318,21 @@ public Boolean getEnableSessionTelemetry() { * whether to enable session telemetry * @return this config instance for method chaining */ - public SessionConfig setEnableSessionTelemetry(Boolean enableSessionTelemetry) { + public SessionConfig setEnableSessionTelemetry(boolean enableSessionTelemetry) { this.enableSessionTelemetry = enableSessionTelemetry; return this; } + /** + * Clears the enableSessionTelemetry setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableSessionTelemetry() { + this.enableSessionTelemetry = null; + return this; + } + /** * Gets the permission request handler. * @@ -658,11 +673,13 @@ public SessionConfig setConfigDir(String configDir) { /** * Gets whether automatic configuration discovery is enabled. * - * @return {@code true} to enable discovery, {@code false} to disable, or - * {@code null} to use the runtime default + * @return an {@link java.util.Optional} containing {@code true} to enable + * discovery or {@code false} to disable, or + * {@link java.util.Optional#empty()} to use the runtime default */ - public Boolean getEnableConfigDiscovery() { - return enableConfigDiscovery; + @JsonIgnore + public Optional getEnableConfigDiscovery() { + return Optional.ofNullable(enableConfigDiscovery); } /** @@ -676,23 +693,34 @@ public Boolean getEnableConfigDiscovery() { * name collision. * * @param enableConfigDiscovery - * {@code true} to enable discovery, {@code false} to disable, or - * {@code null} to use the runtime default + * {@code true} to enable discovery, {@code false} to disable * @return this config instance for method chaining */ - public SessionConfig setEnableConfigDiscovery(Boolean enableConfigDiscovery) { + public SessionConfig setEnableConfigDiscovery(boolean enableConfigDiscovery) { this.enableConfigDiscovery = enableConfigDiscovery; return this; } + /** + * Clears the enableConfigDiscovery setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearEnableConfigDiscovery() { + this.enableConfigDiscovery = null; + return this; + } + /** * Gets whether sub-agent streaming events are included. * - * @return {@code true} to include sub-agent streaming events, {@code false} to - * suppress them, or {@code null} to use the runtime default + * @return an {@link java.util.Optional} containing {@code true} to include + * sub-agent streaming events or {@code false} to suppress them, or + * {@link java.util.Optional#empty()} to use the runtime default */ - public Boolean getIncludeSubAgentStreamingEvents() { - return includeSubAgentStreamingEvents; + @JsonIgnore + public Optional getIncludeSubAgentStreamingEvents() { + return Optional.ofNullable(includeSubAgentStreamingEvents); } /** @@ -709,11 +737,22 @@ public Boolean getIncludeSubAgentStreamingEvents() { * suppress * @return this config instance for method chaining */ - public SessionConfig setIncludeSubAgentStreamingEvents(Boolean includeSubAgentStreamingEvents) { + public SessionConfig setIncludeSubAgentStreamingEvents(boolean includeSubAgentStreamingEvents) { this.includeSubAgentStreamingEvents = includeSubAgentStreamingEvents; return this; } + /** + * Clears the includeSubAgentStreamingEvents setting, reverting to the default + * behavior. + * + * @return this instance for method chaining + */ + public SessionConfig clearIncludeSubAgentStreamingEvents() { + this.includeSubAgentStreamingEvents = null; + return this; + } + /** * Gets the model capabilities override. * @@ -901,6 +940,45 @@ public SessionConfig setGitHubToken(String gitHubToken) { return this; } + /** + * Gets the per-session remote behavior control. + *

+ * Possible values: + *

    + *
  • {@code "off"} — local only, no remote export (default)
  • + *
  • {@code "export"} — export session events to GitHub without enabling + * remote steering
  • + *
  • {@code "on"} — export to GitHub AND enable remote steering
  • + *
+ * + * @return the remote session mode, or {@code null} if not set + * @since 1.4.0 + */ + public String getRemoteSession() { + return remoteSession; + } + + /** + * Sets the per-session remote behavior control. + *

+ * Possible values: + *

    + *
  • {@code "off"} — local only, no remote export (default)
  • + *
  • {@code "export"} — export session events to GitHub without enabling + * remote steering
  • + *
  • {@code "on"} — export to GitHub AND enable remote steering
  • + *
+ * + * @param remoteSession + * the remote session mode + * @return this config instance for method chaining + * @since 1.4.0 + */ + public SessionConfig setRemoteSession(String remoteSession) { + this.remoteSession = remoteSession; + return this; + } + /** * Creates a shallow clone of this {@code SessionConfig} instance. *

@@ -950,6 +1028,7 @@ public SessionConfig clone() { copy.onExitPlanMode = this.onExitPlanMode; copy.onAutoModeSwitch = this.onAutoModeSwitch; copy.gitHubToken = this.gitHubToken; + copy.remoteSession = this.remoteSession; return copy; } } diff --git a/src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java b/src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java index 9b8e0b5872..d19d531eef 100644 --- a/src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java +++ b/src/main/java/com/github/copilot/sdk/json/SessionUiCapabilities.java @@ -4,23 +4,31 @@ package com.github.copilot.sdk.json; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * UI-specific capability flags for a session. * * @since 1.0.0 */ +@JsonInclude(JsonInclude.Include.NON_NULL) public class SessionUiCapabilities { + @JsonProperty("elicitation") private Boolean elicitation; /** * Returns whether the host supports interactive elicitation dialogs. * - * @return {@code true} if elicitation is supported, {@code false} or - * {@code null} otherwise + * @return an {@link Optional} containing the boolean value, or empty if not set */ - public Boolean getElicitation() { - return elicitation; + @JsonIgnore + public Optional getElicitation() { + return Optional.ofNullable(elicitation); } /** @@ -30,8 +38,19 @@ public Boolean getElicitation() { * {@code true} if elicitation is supported * @return this instance for method chaining */ - public SessionUiCapabilities setElicitation(Boolean elicitation) { + public SessionUiCapabilities setElicitation(boolean elicitation) { this.elicitation = elicitation; return this; } + + /** + * Clears the elicitation setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public SessionUiCapabilities clearElicitation() { + this.elicitation = null; + return this; + } + } diff --git a/src/main/java/com/github/copilot/sdk/json/TelemetryConfig.java b/src/main/java/com/github/copilot/sdk/json/TelemetryConfig.java index 8407ab6098..7272c98841 100644 --- a/src/main/java/com/github/copilot/sdk/json/TelemetryConfig.java +++ b/src/main/java/com/github/copilot/sdk/json/TelemetryConfig.java @@ -4,6 +4,11 @@ package com.github.copilot.sdk.json; +import java.util.Optional; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; + /** * OpenTelemetry configuration for the Copilot CLI server. *

@@ -21,6 +26,7 @@ * @see CopilotClientOptions#setTelemetry(TelemetryConfig) * @since 1.2.0 */ +@JsonInclude(JsonInclude.Include.NON_NULL) public class TelemetryConfig { private String otlpEndpoint; @@ -128,11 +134,13 @@ public TelemetryConfig setSourceName(String sourceName) { * Maps to the {@code OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT} * environment variable. * - * @return {@code true} to capture content, {@code false} to suppress it, or - * {@code null} to use the default + * @return an {@link java.util.Optional} containing {@code true} to capture + * content or {@code false} to suppress it, or + * {@link java.util.Optional#empty()} to use the default */ - public Boolean getCaptureContent() { - return captureContent; + @JsonIgnore + public Optional getCaptureContent() { + return Optional.ofNullable(captureContent); } /** @@ -142,8 +150,19 @@ public Boolean getCaptureContent() { * {@code true} to capture content, {@code false} to suppress it * @return this config for method chaining */ - public TelemetryConfig setCaptureContent(Boolean captureContent) { + public TelemetryConfig setCaptureContent(boolean captureContent) { this.captureContent = captureContent; return this; } + + /** + * Clears the captureContent setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public TelemetryConfig clearCaptureContent() { + this.captureContent = null; + return this; + } + } diff --git a/src/main/java/com/github/copilot/sdk/json/UserInputRequest.java b/src/main/java/com/github/copilot/sdk/json/UserInputRequest.java index 9b466f866c..23b0d88123 100644 --- a/src/main/java/com/github/copilot/sdk/json/UserInputRequest.java +++ b/src/main/java/com/github/copilot/sdk/json/UserInputRequest.java @@ -8,7 +8,10 @@ import java.util.List; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.util.Optional; /** * Request for user input from the agent. @@ -19,6 +22,7 @@ * @since 1.0.6 */ @JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) public class UserInputRequest { @JsonProperty("question") @@ -75,11 +79,13 @@ public UserInputRequest setChoices(List choices) { /** * Returns whether freeform text input is allowed. * - * @return {@code true} if freeform input is allowed, {@code null} if not + * @return an {@link java.util.Optional} containing {@code true} if freeform + * input is allowed, or {@link java.util.Optional#empty()} if not * specified */ - public Boolean getAllowFreeform() { - return allowFreeform; + @JsonIgnore + public Optional getAllowFreeform() { + return Optional.ofNullable(allowFreeform); } /** @@ -89,8 +95,19 @@ public Boolean getAllowFreeform() { * {@code true} to allow freeform input * @return this instance for method chaining */ - public UserInputRequest setAllowFreeform(Boolean allowFreeform) { + public UserInputRequest setAllowFreeform(boolean allowFreeform) { this.allowFreeform = allowFreeform; return this; } + + /** + * Clears the allowFreeform setting, reverting to the default behavior. + * + * @return this instance for method chaining + */ + public UserInputRequest clearAllowFreeform() { + this.allowFreeform = null; + return this; + } + } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000000..d912fb420f --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +/** + * GitHub Copilot SDK for Java. + */ +module com.github.copilot.sdk.java { + requires transitive com.fasterxml.jackson.annotation; + requires com.fasterxml.jackson.core; + requires transitive com.fasterxml.jackson.databind; + requires com.fasterxml.jackson.datatype.jsr310; + requires static com.github.spotbugs.annotations; + requires static java.compiler; + requires static java.net.http; + requires java.logging; + + exports com.github.copilot.sdk; + exports com.github.copilot.sdk.generated; + exports com.github.copilot.sdk.generated.rpc; + exports com.github.copilot.sdk.json; + + opens com.github.copilot.sdk to com.fasterxml.jackson.databind; + opens com.github.copilot.sdk.generated to com.fasterxml.jackson.databind; + opens com.github.copilot.sdk.json to com.fasterxml.jackson.databind; +} diff --git a/src/site/markdown/cookbook/error-handling.md b/src/site/markdown/cookbook/error-handling.md index 74732006ee..963b8b0933 100644 --- a/src/site/markdown/cookbook/error-handling.md +++ b/src/site/markdown/cookbook/error-handling.md @@ -30,7 +30,7 @@ jbang BasicErrorHandling.java **Code:** ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.json.MessageOptions; @@ -64,7 +64,7 @@ public class BasicErrorHandling { ## Handling specific error types ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import java.util.concurrent.ExecutionException; @@ -99,7 +99,7 @@ public class SpecificErrorHandling { ## Timeout handling ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotSession; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.json.MessageOptions; @@ -130,7 +130,7 @@ public class TimeoutHandling { ## Aborting a request ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotSession; import com.github.copilot.sdk.json.MessageOptions; import java.util.concurrent.Executors; @@ -162,7 +162,7 @@ public class AbortRequest { ## Graceful shutdown ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; public class GracefulShutdown { @@ -192,7 +192,7 @@ public class GracefulShutdown { ## Try-with-resources pattern ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.json.MessageOptions; @@ -224,7 +224,7 @@ public class TryWithResources { ## Handling tool errors ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.json.MessageOptions; diff --git a/src/site/markdown/cookbook/managing-local-files.md b/src/site/markdown/cookbook/managing-local-files.md index 78f914f103..2a7b5540f9 100644 --- a/src/site/markdown/cookbook/managing-local-files.md +++ b/src/site/markdown/cookbook/managing-local-files.md @@ -34,7 +34,7 @@ jbang ManagingLocalFiles.java **Code:** ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.generated.SessionIdleEvent; @@ -161,7 +161,7 @@ session.send(new MessageOptions().setPrompt(prompt)); ## Interactive file organization ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import java.io.BufferedReader; import java.io.InputStreamReader; diff --git a/src/site/markdown/cookbook/multiple-sessions.md b/src/site/markdown/cookbook/multiple-sessions.md index 42bb1f6222..84da5a2556 100644 --- a/src/site/markdown/cookbook/multiple-sessions.md +++ b/src/site/markdown/cookbook/multiple-sessions.md @@ -30,7 +30,7 @@ jbang MultipleSessions.java **Code:** ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.json.MessageOptions; @@ -123,7 +123,7 @@ try { ## Managing session lifecycle with CompletableFuture ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import java.util.concurrent.CompletableFuture; import java.util.List; diff --git a/src/site/markdown/cookbook/persisting-sessions.md b/src/site/markdown/cookbook/persisting-sessions.md index 119448a440..ef80fd3d09 100644 --- a/src/site/markdown/cookbook/persisting-sessions.md +++ b/src/site/markdown/cookbook/persisting-sessions.md @@ -30,7 +30,7 @@ jbang PersistingSessions.java **Code:** ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.json.MessageOptions; @@ -127,7 +127,7 @@ public class DeleteSession { ## Getting session history ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.generated.UserMessageEvent; @@ -162,7 +162,7 @@ public class SessionHistory { ## Complete example with session management ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import java.util.Scanner; public class SessionManager { diff --git a/src/site/markdown/cookbook/pr-visualization.md b/src/site/markdown/cookbook/pr-visualization.md index 627ee5b842..1036bb0f7d 100644 --- a/src/site/markdown/cookbook/pr-visualization.md +++ b/src/site/markdown/cookbook/pr-visualization.md @@ -34,7 +34,7 @@ jbang PRVisualization.java github/copilot-sdk ## Full example: PRVisualization.java ```java -//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.3-beta-java.2-beta-java.1 +//DEPS com.github:copilot-sdk-java:1.0.0-beta-java.4 import com.github.copilot.sdk.CopilotClient; import com.github.copilot.sdk.generated.AssistantMessageEvent; import com.github.copilot.sdk.generated.ToolExecutionStartEvent; diff --git a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java index 7abc47e4a4..09bd3ee385 100644 --- a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java +++ b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java @@ -203,7 +203,7 @@ void sessionConfigEnableSessionTelemetryCopied() { SessionConfig cloned = original.clone(); - assertFalse(cloned.getEnableSessionTelemetry()); + assertFalse(cloned.getEnableSessionTelemetry().orElse(true)); } @Test @@ -212,7 +212,7 @@ void sessionConfigEnableSessionTelemetryDefaultIsNull() { SessionConfig cloned = original.clone(); - assertNull(cloned.getEnableSessionTelemetry()); + assertTrue(cloned.getEnableSessionTelemetry().isEmpty()); } @Test @@ -222,7 +222,7 @@ void resumeSessionConfigEnableSessionTelemetryCopied() { ResumeSessionConfig cloned = original.clone(); - assertFalse(cloned.getEnableSessionTelemetry()); + assertFalse(cloned.getEnableSessionTelemetry().orElse(true)); } @Test @@ -231,7 +231,7 @@ void resumeSessionConfigEnableSessionTelemetryDefaultIsNull() { ResumeSessionConfig cloned = original.clone(); - assertNull(cloned.getEnableSessionTelemetry()); + assertTrue(cloned.getEnableSessionTelemetry().isEmpty()); } @Test @@ -301,11 +301,11 @@ void copilotClientOptionsSetTelemetry() { } @Test - void copilotClientOptionsSetUseLoggedInUserNull() { + void copilotClientOptionsClearUseLoggedInUser() { var opts = new CopilotClientOptions(); - opts.setUseLoggedInUser(null); - // null → Boolean.FALSE - assertEquals(Boolean.FALSE, opts.getUseLoggedInUser()); + opts.setUseLoggedInUser(true); + opts.clearUseLoggedInUser(); + assertTrue(opts.getUseLoggedInUser().isEmpty()); } @Test @@ -375,7 +375,7 @@ void copilotClientOptionsSessionIdleTimeoutCloned() { CopilotClientOptions cloned = original.clone(); - assertEquals(600, cloned.getSessionIdleTimeoutSeconds()); + assertEquals(600, cloned.getSessionIdleTimeoutSeconds().getAsInt()); } @Test diff --git a/src/test/java/com/github/copilot/sdk/CopilotClientTest.java b/src/test/java/com/github/copilot/sdk/CopilotClientTest.java index b0430959b9..14ed8ca89e 100644 --- a/src/test/java/com/github/copilot/sdk/CopilotClientTest.java +++ b/src/test/java/com/github/copilot/sdk/CopilotClientTest.java @@ -20,6 +20,7 @@ import java.util.concurrent.ExecutionException; import static org.junit.jupiter.api.Assertions.*; +import java.util.Optional; /** * Tests for CopilotClient. @@ -153,14 +154,14 @@ void testGitHubTokenOptionAccepted() { void testUseLoggedInUserDefaultsToNull() { var options = new CopilotClientOptions().setCliPath("/path/to/cli"); - assertNull(options.getUseLoggedInUser()); + assertTrue(options.getUseLoggedInUser().isEmpty()); } @Test void testExplicitUseLoggedInUserFalse() { var options = new CopilotClientOptions().setCliPath("/path/to/cli").setUseLoggedInUser(false); - assertEquals(false, options.getUseLoggedInUser()); + assertEquals(Optional.of(false), options.getUseLoggedInUser()); } @Test @@ -168,7 +169,7 @@ void testExplicitUseLoggedInUserTrueWithGitHubToken() { var options = new CopilotClientOptions().setCliPath("/path/to/cli").setGitHubToken("gho_test_token") .setUseLoggedInUser(true); - assertEquals(true, options.getUseLoggedInUser()); + assertEquals(Optional.of(true), options.getUseLoggedInUser()); } @Test @@ -189,14 +190,14 @@ void testUseLoggedInUserWithCliUrlThrows() { void testSessionIdleTimeoutSecondsDefaultsToNull() { var options = new CopilotClientOptions(); - assertNull(options.getSessionIdleTimeoutSeconds()); + assertTrue(options.getSessionIdleTimeoutSeconds().isEmpty()); } @Test void testSessionIdleTimeoutSecondsOptionAccepted() { var options = new CopilotClientOptions().setSessionIdleTimeoutSeconds(600); - assertEquals(600, options.getSessionIdleTimeoutSeconds()); + assertEquals(600, options.getSessionIdleTimeoutSeconds().getAsInt()); } @Test diff --git a/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java b/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java index b61c93559b..2ff3600200 100644 --- a/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java +++ b/src/test/java/com/github/copilot/sdk/DataObjectCoverageTest.java @@ -217,17 +217,16 @@ void mcpStdioServerConfigCoversGettersAndFluentSetters() { @Test void modelCapabilitiesOverrideCoversNestedSupportsAndLimits() { - var supports = new ModelCapabilitiesOverride.Supports().setVision(Boolean.TRUE) - .setReasoningEffort(Boolean.FALSE); + var supports = new ModelCapabilitiesOverride.Supports().setVision(true).setReasoningEffort(false); var limits = new ModelCapabilitiesOverride.Limits().setMaxPromptTokens(2048).setMaxOutputTokens(512) .setMaxContextWindowTokens(8192); var override = new ModelCapabilitiesOverride().setSupports(supports).setLimits(limits); - assertTrue(override.getSupports().getVision()); - assertFalse(override.getSupports().getReasoningEffort()); - assertEquals(2048, override.getLimits().getMaxPromptTokens()); - assertEquals(512, override.getLimits().getMaxOutputTokens()); - assertEquals(8192, override.getLimits().getMaxContextWindowTokens()); + assertTrue(override.getSupports().getVision().orElse(false)); + assertFalse(override.getSupports().getReasoningEffort().orElse(true)); + assertEquals(2048, override.getLimits().getMaxPromptTokens().getAsInt()); + assertEquals(512, override.getLimits().getMaxOutputTokens().getAsInt()); + assertEquals(8192, override.getLimits().getMaxContextWindowTokens().getAsInt()); } } diff --git a/src/test/java/com/github/copilot/sdk/ElicitationTest.java b/src/test/java/com/github/copilot/sdk/ElicitationTest.java index 6b748b4cc2..1f62451276 100644 --- a/src/test/java/com/github/copilot/sdk/ElicitationTest.java +++ b/src/test/java/com/github/copilot/sdk/ElicitationTest.java @@ -40,7 +40,7 @@ void sessionCapabilitiesTypesAreProperlyStructured() { var capabilities = new SessionCapabilities().setUi(new SessionUiCapabilities().setElicitation(true)); assertNotNull(capabilities.getUi()); - assertTrue(capabilities.getUi().getElicitation()); + assertTrue(capabilities.getUi().getElicitation().get()); // Test with null UI var emptyCapabilities = new SessionCapabilities(); @@ -119,8 +119,8 @@ void inputOptionsHasAllFields() { assertEquals("My Title", opts.getTitle()); assertEquals("My Desc", opts.getDescription()); - assertEquals(1, opts.getMinLength()); - assertEquals(100, opts.getMaxLength()); + assertEquals(1, opts.getMinLength().getAsInt()); + assertEquals(100, opts.getMaxLength().getAsInt()); assertEquals("email", opts.getFormat()); assertEquals("default@example.com", opts.getDefaultValue()); } @@ -164,7 +164,7 @@ void buildCreateRequestSetsRequestElicitationWhenHandlerPresent() { var request = SessionRequestBuilder.buildCreateRequest(config); - assertTrue(Boolean.TRUE.equals(request.getRequestElicitation())); + assertEquals(Boolean.TRUE, request.getRequestElicitation()); } @Test @@ -186,6 +186,6 @@ void buildResumeRequestSetsRequestElicitationWhenHandlerPresent() { var request = SessionRequestBuilder.buildResumeRequest("session-1", config); - assertTrue(Boolean.TRUE.equals(request.getRequestElicitation())); + assertEquals(Boolean.TRUE, request.getRequestElicitation()); } } diff --git a/src/test/java/com/github/copilot/sdk/JsonIncludeNonNullTest.java b/src/test/java/com/github/copilot/sdk/JsonIncludeNonNullTest.java new file mode 100644 index 0000000000..7465507f50 --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/JsonIncludeNonNullTest.java @@ -0,0 +1,159 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.sdk; + +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.Test; + +import com.github.copilot.sdk.json.CopilotClientOptions; +import com.github.copilot.sdk.json.CustomAgentConfig; +import com.github.copilot.sdk.json.InfiniteSessionConfig; +import com.github.copilot.sdk.json.InputOptions; +import com.github.copilot.sdk.json.ModelCapabilitiesOverride; +import com.github.copilot.sdk.json.ProviderConfig; +import com.github.copilot.sdk.json.ResumeSessionConfig; +import com.github.copilot.sdk.json.SessionConfig; +import com.github.copilot.sdk.json.SessionUiCapabilities; +import com.github.copilot.sdk.json.TelemetryConfig; +import com.github.copilot.sdk.json.UserInputRequest; + +/** + * Verifies that public DTO classes in the {@code com.github.copilot.sdk.json} + * package are annotated with {@code @JsonInclude(JsonInclude.Include.NON_NULL)} + * so that null-valued fields are omitted during JSON serialization. + */ +class JsonIncludeNonNullTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + // --- Annotation presence checks --- + + @Test + void copilotClientOptionsHasNonNullAnnotation() { + assertHasNonNullInclude(CopilotClientOptions.class); + } + + @Test + void sessionConfigHasNonNullAnnotation() { + assertHasNonNullInclude(SessionConfig.class); + } + + @Test + void resumeSessionConfigHasNonNullAnnotation() { + assertHasNonNullInclude(ResumeSessionConfig.class); + } + + @Test + void infiniteSessionConfigHasNonNullAnnotation() { + assertHasNonNullInclude(InfiniteSessionConfig.class); + } + + @Test + void inputOptionsHasNonNullAnnotation() { + assertHasNonNullInclude(InputOptions.class); + } + + @Test + void modelCapabilitiesOverrideHasNonNullAnnotation() { + assertHasNonNullInclude(ModelCapabilitiesOverride.class); + } + + @Test + void providerConfigHasNonNullAnnotation() { + assertHasNonNullInclude(ProviderConfig.class); + } + + @Test + void telemetryConfigHasNonNullAnnotation() { + assertHasNonNullInclude(TelemetryConfig.class); + } + + @Test + void sessionUiCapabilitiesHasNonNullAnnotation() { + assertHasNonNullInclude(SessionUiCapabilities.class); + } + + @Test + void customAgentConfigHasNonNullAnnotation() { + assertHasNonNullInclude(CustomAgentConfig.class); + } + + @Test + void userInputRequestHasNonNullAnnotation() { + assertHasNonNullInclude(UserInputRequest.class); + } + + // --- Serialization tests: null fields are omitted --- + + @Test + void inputOptionsOmitsNullFieldsInJson() throws JsonProcessingException { + var opts = new InputOptions(); + String json = MAPPER.writeValueAsString(opts); + assertEquals("{}", json, "All-null InputOptions should serialize to empty JSON"); + } + + @Test + void telemetryConfigOmitsNullFieldsInJson() throws JsonProcessingException { + var config = new TelemetryConfig(); + String json = MAPPER.writeValueAsString(config); + assertEquals("{}", json, "All-null TelemetryConfig should serialize to empty JSON"); + } + + @Test + void sessionUiCapabilitiesOmitsNullFieldsInJson() throws JsonProcessingException { + var caps = new SessionUiCapabilities(); + String json = MAPPER.writeValueAsString(caps); + assertEquals("{}", json, "All-null SessionUiCapabilities should serialize to empty JSON"); + } + + @Test + void userInputRequestOmitsNullFieldsInJson() throws JsonProcessingException { + var req = new UserInputRequest(); + String json = MAPPER.writeValueAsString(req); + assertFalse(json.contains("null"), "UserInputRequest with no fields set should not contain 'null' values"); + } + + @Test + void inputOptionsIncludesSetFieldsInJson() throws JsonProcessingException { + var opts = new InputOptions(); + opts.setMinLength(5); + opts.setMaxLength(100); + String json = MAPPER.writeValueAsString(opts); + assertTrue(json.contains("\"minLength\":5"), "Set minLength should appear in JSON"); + assertTrue(json.contains("\"maxLength\":100"), "Set maxLength should appear in JSON"); + assertFalse(json.contains("\"title\""), "Unset title should be omitted from JSON"); + } + + @Test + void telemetryConfigIncludesSetFieldsInJson() throws JsonProcessingException { + var config = new TelemetryConfig(); + config.setOtlpEndpoint("http://localhost:4318"); + String json = MAPPER.writeValueAsString(config); + assertTrue(json.contains("\"otlpEndpoint\":\"http://localhost:4318\""), + "Set otlpEndpoint should appear in JSON"); + assertFalse(json.contains("\"filePath\""), "Unset filePath should be omitted from JSON"); + } + + @Test + void sessionUiCapabilitiesIncludesSetFieldsInJson() throws JsonProcessingException { + var caps = new SessionUiCapabilities(); + caps.setElicitation(true); + String json = MAPPER.writeValueAsString(caps); + assertTrue(json.contains("\"elicitation\":true"), "Set elicitation should appear in JSON"); + } + + private void assertHasNonNullInclude(Class clazz) { + JsonInclude annotation = clazz.getAnnotation(JsonInclude.class); + assertNotNull(annotation, clazz.getSimpleName() + " should be annotated with @JsonInclude"); + assertEquals(JsonInclude.Include.NON_NULL, annotation.value(), + clazz.getSimpleName() + " @JsonInclude should use Include.NON_NULL"); + } + +} diff --git a/src/test/java/com/github/copilot/sdk/ModuleDescriptorTest.java b/src/test/java/com/github/copilot/sdk/ModuleDescriptorTest.java new file mode 100644 index 0000000000..36be137345 --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/ModuleDescriptorTest.java @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.sdk; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.module.ModuleDescriptor; +import org.junit.jupiter.api.Test; + +class ModuleDescriptorTest { + + @Test + void sdkHasExplicitModuleDescriptor() { + Module module = CopilotClient.class.getModule(); + assertTrue(module.isNamed()); + assertEquals("com.github.copilot.sdk.java", module.getName()); + + ModuleDescriptor descriptor = module.getDescriptor(); + assertTrue(descriptor.exports().stream().anyMatch(export -> export.source().equals("com.github.copilot.sdk"))); + assertTrue(descriptor.exports().stream() + .anyMatch(export -> export.source().equals("com.github.copilot.sdk.json"))); + assertTrue(descriptor.requires().stream() + .anyMatch(require -> require.name().equals("com.fasterxml.jackson.databind"))); + } +} diff --git a/src/test/java/com/github/copilot/sdk/OptionalApiAndJacksonTest.java b/src/test/java/com/github/copilot/sdk/OptionalApiAndJacksonTest.java new file mode 100644 index 0000000000..2a9770e2e2 --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/OptionalApiAndJacksonTest.java @@ -0,0 +1,635 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.sdk; + +import static org.junit.jupiter.api.Assertions.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.sdk.json.CopilotClientOptions; +import com.github.copilot.sdk.json.CustomAgentConfig; +import com.github.copilot.sdk.json.InfiniteSessionConfig; +import com.github.copilot.sdk.json.InputOptions; +import com.github.copilot.sdk.json.ModelCapabilitiesOverride; +import com.github.copilot.sdk.json.ProviderConfig; +import com.github.copilot.sdk.json.ResumeSessionConfig; +import com.github.copilot.sdk.json.SessionConfig; +import com.github.copilot.sdk.json.SessionUiCapabilities; +import com.github.copilot.sdk.json.TelemetryConfig; +import com.github.copilot.sdk.json.UserInputRequest; +import org.junit.jupiter.api.Test; + +/** + * Validates that every {@code clearXxx()} method resets its field to absent, + * that Optional-returning getters report the correct state, and that Jackson + * omits cleared fields from serialized output. + */ +class OptionalApiAndJacksonTest { + + private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); + + // ── CopilotClientOptions ────────────────────────────────────────── + + @Test + void copilotClientOptions_clearSessionIdleTimeoutSeconds() { + var opts = new CopilotClientOptions(); + opts.setSessionIdleTimeoutSeconds(120); + assertFalse(opts.getSessionIdleTimeoutSeconds().isEmpty()); + + opts.clearSessionIdleTimeoutSeconds(); + assertTrue(opts.getSessionIdleTimeoutSeconds().isEmpty()); + } + + @Test + void copilotClientOptions_clearUseLoggedInUser() { + var opts = new CopilotClientOptions(); + opts.setUseLoggedInUser(true); + assertTrue(opts.getUseLoggedInUser().isPresent()); + + opts.clearUseLoggedInUser(); + assertTrue(opts.getUseLoggedInUser().isEmpty()); + } + + // ── SessionConfig ───────────────────────────────────────────────── + + @Test + void sessionConfig_clearEnableSessionTelemetry() { + var cfg = new SessionConfig(); + cfg.setEnableSessionTelemetry(true); + assertTrue(cfg.getEnableSessionTelemetry().isPresent()); + + cfg.clearEnableSessionTelemetry(); + assertTrue(cfg.getEnableSessionTelemetry().isEmpty()); + } + + @Test + void sessionConfig_clearEnableConfigDiscovery() { + var cfg = new SessionConfig(); + cfg.setEnableConfigDiscovery(false); + assertTrue(cfg.getEnableConfigDiscovery().isPresent()); + + cfg.clearEnableConfigDiscovery(); + assertTrue(cfg.getEnableConfigDiscovery().isEmpty()); + } + + @Test + void sessionConfig_clearIncludeSubAgentStreamingEvents() { + var cfg = new SessionConfig(); + cfg.setIncludeSubAgentStreamingEvents(true); + assertTrue(cfg.getIncludeSubAgentStreamingEvents().isPresent()); + + cfg.clearIncludeSubAgentStreamingEvents(); + assertTrue(cfg.getIncludeSubAgentStreamingEvents().isEmpty()); + } + + // ── ResumeSessionConfig ─────────────────────────────────────────── + + @Test + void resumeSessionConfig_clearEnableSessionTelemetry() { + var cfg = new ResumeSessionConfig(); + cfg.setEnableSessionTelemetry(false); + assertTrue(cfg.getEnableSessionTelemetry().isPresent()); + + cfg.clearEnableSessionTelemetry(); + assertTrue(cfg.getEnableSessionTelemetry().isEmpty()); + } + + @Test + void resumeSessionConfig_clearEnableConfigDiscovery() { + var cfg = new ResumeSessionConfig(); + cfg.setEnableConfigDiscovery(true); + assertTrue(cfg.getEnableConfigDiscovery().isPresent()); + + cfg.clearEnableConfigDiscovery(); + assertTrue(cfg.getEnableConfigDiscovery().isEmpty()); + } + + @Test + void resumeSessionConfig_clearIncludeSubAgentStreamingEvents() { + var cfg = new ResumeSessionConfig(); + cfg.setIncludeSubAgentStreamingEvents(false); + assertTrue(cfg.getIncludeSubAgentStreamingEvents().isPresent()); + + cfg.clearIncludeSubAgentStreamingEvents(); + assertTrue(cfg.getIncludeSubAgentStreamingEvents().isEmpty()); + } + + // ── InfiniteSessionConfig ───────────────────────────────────────── + + @Test + void infiniteSessionConfig_clearEnabled() { + var cfg = new InfiniteSessionConfig(); + cfg.setEnabled(true); + assertTrue(cfg.getEnabled().isPresent()); + + cfg.clearEnabled(); + assertTrue(cfg.getEnabled().isEmpty()); + } + + @Test + void infiniteSessionConfig_clearBackgroundCompactionThreshold() { + var cfg = new InfiniteSessionConfig(); + cfg.setBackgroundCompactionThreshold(0.75); + assertFalse(cfg.getBackgroundCompactionThreshold().isEmpty()); + + cfg.clearBackgroundCompactionThreshold(); + assertTrue(cfg.getBackgroundCompactionThreshold().isEmpty()); + } + + @Test + void infiniteSessionConfig_clearBufferExhaustionThreshold() { + var cfg = new InfiniteSessionConfig(); + cfg.setBufferExhaustionThreshold(0.9); + assertFalse(cfg.getBufferExhaustionThreshold().isEmpty()); + + cfg.clearBufferExhaustionThreshold(); + assertTrue(cfg.getBufferExhaustionThreshold().isEmpty()); + } + + // ── InputOptions ────────────────────────────────────────────────── + + @Test + void inputOptions_clearMinLength() { + var opts = new InputOptions(); + opts.setMinLength(5); + assertFalse(opts.getMinLength().isEmpty()); + + opts.clearMinLength(); + assertTrue(opts.getMinLength().isEmpty()); + } + + @Test + void inputOptions_clearMaxLength() { + var opts = new InputOptions(); + opts.setMaxLength(100); + assertFalse(opts.getMaxLength().isEmpty()); + + opts.clearMaxLength(); + assertTrue(opts.getMaxLength().isEmpty()); + } + + // ── ModelCapabilitiesOverride.Supports ───────────────────────────── + + @Test + void supports_clearVision() { + var s = new ModelCapabilitiesOverride.Supports(); + s.setVision(true); + assertTrue(s.getVision().isPresent()); + + s.clearVision(); + assertTrue(s.getVision().isEmpty()); + } + + @Test + void supports_clearReasoningEffort() { + var s = new ModelCapabilitiesOverride.Supports(); + s.setReasoningEffort(false); + assertTrue(s.getReasoningEffort().isPresent()); + + s.clearReasoningEffort(); + assertTrue(s.getReasoningEffort().isEmpty()); + } + + // ── ModelCapabilitiesOverride.Limits ─────────────────────────────── + + @Test + void limits_clearMaxPromptTokens() { + var l = new ModelCapabilitiesOverride.Limits(); + l.setMaxPromptTokens(4096); + assertFalse(l.getMaxPromptTokens().isEmpty()); + + l.clearMaxPromptTokens(); + assertTrue(l.getMaxPromptTokens().isEmpty()); + } + + @Test + void limits_clearMaxOutputTokens() { + var l = new ModelCapabilitiesOverride.Limits(); + l.setMaxOutputTokens(1024); + assertFalse(l.getMaxOutputTokens().isEmpty()); + + l.clearMaxOutputTokens(); + assertTrue(l.getMaxOutputTokens().isEmpty()); + } + + @Test + void limits_clearMaxContextWindowTokens() { + var l = new ModelCapabilitiesOverride.Limits(); + l.setMaxContextWindowTokens(16384); + assertFalse(l.getMaxContextWindowTokens().isEmpty()); + + l.clearMaxContextWindowTokens(); + assertTrue(l.getMaxContextWindowTokens().isEmpty()); + } + + // ── ProviderConfig ──────────────────────────────────────────────── + + @Test + void providerConfig_clearMaxPromptTokens() { + var cfg = new ProviderConfig(); + cfg.setMaxPromptTokens(2048); + assertFalse(cfg.getMaxPromptTokens().isEmpty()); + + cfg.clearMaxPromptTokens(); + assertTrue(cfg.getMaxPromptTokens().isEmpty()); + } + + @Test + void providerConfig_clearMaxOutputTokens() { + var cfg = new ProviderConfig(); + cfg.setMaxOutputTokens(512); + assertFalse(cfg.getMaxOutputTokens().isEmpty()); + + cfg.clearMaxOutputTokens(); + assertTrue(cfg.getMaxOutputTokens().isEmpty()); + } + + // ── TelemetryConfig ─────────────────────────────────────────────── + + @Test + void telemetryConfig_clearCaptureContent() { + var cfg = new TelemetryConfig(); + cfg.setCaptureContent(true); + assertTrue(cfg.getCaptureContent().isPresent()); + + cfg.clearCaptureContent(); + assertTrue(cfg.getCaptureContent().isEmpty()); + } + + // ── SessionUiCapabilities ───────────────────────────────────────── + + @Test + void sessionUiCapabilities_clearElicitation() { + var caps = new SessionUiCapabilities(); + caps.setElicitation(true); + assertTrue(caps.getElicitation().isPresent()); + + caps.clearElicitation(); + assertTrue(caps.getElicitation().isEmpty()); + } + + // ── CustomAgentConfig ───────────────────────────────────────────── + + @Test + void customAgentConfig_clearInfer() { + var cfg = new CustomAgentConfig(); + cfg.setInfer(true); + assertTrue(cfg.getInfer().isPresent()); + + cfg.clearInfer(); + assertTrue(cfg.getInfer().isEmpty()); + } + + // ── UserInputRequest ────────────────────────────────────────────── + + @Test + void userInputRequest_clearAllowFreeform() { + var req = new UserInputRequest(); + req.setAllowFreeform(false); + assertTrue(req.getAllowFreeform().isPresent()); + + req.clearAllowFreeform(); + assertTrue(req.getAllowFreeform().isEmpty()); + } + + // ── Value retrieval through Optional getters ──────────────────────── + + @Test + void copilotClientOptions_sessionIdleTimeoutSecondsValue() { + var opts = new CopilotClientOptions(); + assertTrue(opts.getSessionIdleTimeoutSeconds().isEmpty()); + + opts.setSessionIdleTimeoutSeconds(300); + assertEquals(300, opts.getSessionIdleTimeoutSeconds().getAsInt()); + + opts.setSessionIdleTimeoutSeconds(0); + assertTrue(opts.getSessionIdleTimeoutSeconds().isPresent()); + assertEquals(0, opts.getSessionIdleTimeoutSeconds().getAsInt()); + } + + @Test + void copilotClientOptions_useLoggedInUserValue() { + var opts = new CopilotClientOptions(); + assertTrue(opts.getUseLoggedInUser().isEmpty()); + + opts.setUseLoggedInUser(true); + assertEquals(Boolean.TRUE, opts.getUseLoggedInUser().get()); + + opts.setUseLoggedInUser(false); + assertEquals(Boolean.FALSE, opts.getUseLoggedInUser().get()); + } + + @Test + void sessionConfig_enableSessionTelemetryValue() { + var cfg = new SessionConfig(); + assertFalse(cfg.getEnableSessionTelemetry().orElse(false)); + + cfg.setEnableSessionTelemetry(true); + assertTrue(cfg.getEnableSessionTelemetry().orElse(false)); + + cfg.setEnableSessionTelemetry(false); + assertFalse(cfg.getEnableSessionTelemetry().orElse(true)); + } + + @Test + void sessionConfig_enableConfigDiscoveryValue() { + var cfg = new SessionConfig(); + assertTrue(cfg.getEnableConfigDiscovery().isEmpty()); + + cfg.setEnableConfigDiscovery(true); + assertTrue(cfg.getEnableConfigDiscovery().get()); + + cfg.setEnableConfigDiscovery(false); + assertFalse(cfg.getEnableConfigDiscovery().get()); + } + + @Test + void sessionConfig_includeSubAgentStreamingEventsValue() { + var cfg = new SessionConfig(); + assertTrue(cfg.getIncludeSubAgentStreamingEvents().isEmpty()); + + cfg.setIncludeSubAgentStreamingEvents(true); + assertTrue(cfg.getIncludeSubAgentStreamingEvents().get()); + } + + @Test + void resumeSessionConfig_enableSessionTelemetryValue() { + var cfg = new ResumeSessionConfig(); + assertTrue(cfg.getEnableSessionTelemetry().isEmpty()); + + cfg.setEnableSessionTelemetry(true); + assertTrue(cfg.getEnableSessionTelemetry().get()); + + cfg.setEnableSessionTelemetry(false); + assertFalse(cfg.getEnableSessionTelemetry().get()); + } + + @Test + void resumeSessionConfig_enableConfigDiscoveryValue() { + var cfg = new ResumeSessionConfig(); + assertTrue(cfg.getEnableConfigDiscovery().isEmpty()); + + cfg.setEnableConfigDiscovery(true); + assertTrue(cfg.getEnableConfigDiscovery().get()); + } + + @Test + void resumeSessionConfig_includeSubAgentStreamingEventsValue() { + var cfg = new ResumeSessionConfig(); + assertTrue(cfg.getIncludeSubAgentStreamingEvents().isEmpty()); + + cfg.setIncludeSubAgentStreamingEvents(false); + assertFalse(cfg.getIncludeSubAgentStreamingEvents().get()); + } + + @Test + void infiniteSessionConfig_thresholdValues() { + var cfg = new InfiniteSessionConfig(); + assertTrue(cfg.getBackgroundCompactionThreshold().isEmpty()); + assertTrue(cfg.getBufferExhaustionThreshold().isEmpty()); + + cfg.setBackgroundCompactionThreshold(0.6); + cfg.setBufferExhaustionThreshold(0.85); + assertEquals(0.6, cfg.getBackgroundCompactionThreshold().getAsDouble(), 0.001); + assertEquals(0.85, cfg.getBufferExhaustionThreshold().getAsDouble(), 0.001); + } + + @Test + void infiniteSessionConfig_enabledValue() { + var cfg = new InfiniteSessionConfig(); + assertTrue(cfg.getEnabled().isEmpty()); + + cfg.setEnabled(true); + assertTrue(cfg.getEnabled().get()); + + cfg.setEnabled(false); + assertFalse(cfg.getEnabled().get()); + } + + @Test + void inputOptions_minAndMaxLengthValues() { + var opts = new InputOptions(); + assertTrue(opts.getMinLength().isEmpty()); + assertTrue(opts.getMaxLength().isEmpty()); + + opts.setMinLength(1); + opts.setMaxLength(255); + assertEquals(1, opts.getMinLength().getAsInt()); + assertEquals(255, opts.getMaxLength().getAsInt()); + } + + @Test + void supports_visionAndReasoningEffortValues() { + var s = new ModelCapabilitiesOverride.Supports(); + assertTrue(s.getVision().isEmpty()); + assertTrue(s.getReasoningEffort().isEmpty()); + + s.setVision(true); + s.setReasoningEffort(false); + assertTrue(s.getVision().get()); + assertFalse(s.getReasoningEffort().get()); + } + + @Test + void limits_tokenValues() { + var l = new ModelCapabilitiesOverride.Limits(); + assertTrue(l.getMaxPromptTokens().isEmpty()); + assertTrue(l.getMaxOutputTokens().isEmpty()); + assertTrue(l.getMaxContextWindowTokens().isEmpty()); + + l.setMaxPromptTokens(4096); + l.setMaxOutputTokens(1024); + l.setMaxContextWindowTokens(16384); + assertEquals(4096, l.getMaxPromptTokens().getAsInt()); + assertEquals(1024, l.getMaxOutputTokens().getAsInt()); + assertEquals(16384, l.getMaxContextWindowTokens().getAsInt()); + } + + @Test + void providerConfig_tokenValues() { + var cfg = new ProviderConfig(); + assertTrue(cfg.getMaxPromptTokens().isEmpty()); + assertTrue(cfg.getMaxOutputTokens().isEmpty()); + + cfg.setMaxPromptTokens(8192); + cfg.setMaxOutputTokens(2048); + assertEquals(8192, cfg.getMaxPromptTokens().getAsInt()); + assertEquals(2048, cfg.getMaxOutputTokens().getAsInt()); + } + + @Test + void telemetryConfig_captureContentValue() { + var cfg = new TelemetryConfig(); + assertTrue(cfg.getCaptureContent().isEmpty()); + + cfg.setCaptureContent(true); + assertTrue(cfg.getCaptureContent().get()); + + cfg.setCaptureContent(false); + assertFalse(cfg.getCaptureContent().get()); + } + + @Test + void sessionUiCapabilities_elicitationValue() { + var caps = new SessionUiCapabilities(); + assertTrue(caps.getElicitation().isEmpty()); + assertFalse(caps.getElicitation().orElse(false)); + + caps.setElicitation(true); + assertTrue(caps.getElicitation().orElse(false)); + } + + @Test + void customAgentConfig_inferValue() { + var cfg = new CustomAgentConfig(); + assertTrue(cfg.getInfer().isEmpty()); + + cfg.setInfer(true); + assertTrue(cfg.getInfer().get()); + + cfg.setInfer(false); + assertFalse(cfg.getInfer().get()); + } + + @Test + void userInputRequest_allowFreeformValue() { + var req = new UserInputRequest(); + assertTrue(req.getAllowFreeform().isEmpty()); + + req.setAllowFreeform(true); + assertTrue(req.getAllowFreeform().get()); + + req.setAllowFreeform(false); + assertFalse(req.getAllowFreeform().get()); + } + + // ── JSON deserialization into Optional-returning classes ─────────── + + @Test + void jackson_deserializeSupportsWithFields() throws Exception { + String json = "{\"vision\":true,\"reasoningEffort\":false}"; + var supports = MAPPER.readValue(json, ModelCapabilitiesOverride.Supports.class); + assertTrue(supports.getVision().get()); + assertFalse(supports.getReasoningEffort().get()); + } + + @Test + void jackson_deserializeSupportsEmpty() throws Exception { + String json = "{}"; + var supports = MAPPER.readValue(json, ModelCapabilitiesOverride.Supports.class); + assertTrue(supports.getVision().isEmpty()); + assertTrue(supports.getReasoningEffort().isEmpty()); + } + + @Test + void jackson_deserializeLimitsWithFields() throws Exception { + String json = "{\"max_prompt_tokens\":4096,\"max_output_tokens\":1024,\"max_context_window_tokens\":16384}"; + var limits = MAPPER.readValue(json, ModelCapabilitiesOverride.Limits.class); + assertEquals(4096, limits.getMaxPromptTokens().getAsInt()); + assertEquals(1024, limits.getMaxOutputTokens().getAsInt()); + assertEquals(16384, limits.getMaxContextWindowTokens().getAsInt()); + } + + @Test + void jackson_deserializeLimitsEmpty() throws Exception { + String json = "{}"; + var limits = MAPPER.readValue(json, ModelCapabilitiesOverride.Limits.class); + assertTrue(limits.getMaxPromptTokens().isEmpty()); + assertTrue(limits.getMaxOutputTokens().isEmpty()); + assertTrue(limits.getMaxContextWindowTokens().isEmpty()); + } + + @Test + void jackson_deserializeInfiniteSessionConfigWithFields() throws Exception { + String json = "{\"enabled\":true,\"backgroundCompactionThreshold\":0.7,\"bufferExhaustionThreshold\":0.9}"; + var cfg = MAPPER.readValue(json, InfiniteSessionConfig.class); + assertTrue(cfg.getEnabled().get()); + assertEquals(0.7, cfg.getBackgroundCompactionThreshold().getAsDouble(), 0.001); + assertEquals(0.9, cfg.getBufferExhaustionThreshold().getAsDouble(), 0.001); + } + + @Test + void jackson_deserializeInfiniteSessionConfigEmpty() throws Exception { + String json = "{}"; + var cfg = MAPPER.readValue(json, InfiniteSessionConfig.class); + assertTrue(cfg.getEnabled().isEmpty()); + assertTrue(cfg.getBackgroundCompactionThreshold().isEmpty()); + assertTrue(cfg.getBufferExhaustionThreshold().isEmpty()); + } + + // ── Jackson serialization roundtrip ─────────────────────────────── + // + // Classes whose fields carry @JsonProperty (InfiniteSessionConfig, + // ModelCapabilitiesOverride inner classes) are serialized via field + // access: Jackson writes the field when set and omits it when cleared. + // + // Classes without @JsonProperty on fields (SessionConfig, + // CopilotClientOptions, TelemetryConfig, ProviderConfig) are not + // directly serialized — their values are copied to wire DTOs by + // SessionRequestBuilder. The @JsonIgnore on their Optional-returning + // getters prevents Jackson from attempting to serialize Optional + // wrappers if the class is ever processed. + + @Test + void jackson_infiniteSessionConfigClearedFieldsOmitted() throws Exception { + var cfg = new InfiniteSessionConfig(); + cfg.setEnabled(true); + cfg.setBackgroundCompactionThreshold(0.75); + cfg.setBufferExhaustionThreshold(0.9); + + String withFields = MAPPER.writeValueAsString(cfg); + assertTrue(withFields.contains("enabled")); + assertTrue(withFields.contains("backgroundCompactionThreshold")); + assertTrue(withFields.contains("bufferExhaustionThreshold")); + + cfg.clearEnabled(); + cfg.clearBackgroundCompactionThreshold(); + cfg.clearBufferExhaustionThreshold(); + + String cleared = MAPPER.writeValueAsString(cfg); + assertFalse(cleared.contains("enabled")); + assertFalse(cleared.contains("backgroundCompactionThreshold")); + assertFalse(cleared.contains("bufferExhaustionThreshold")); + } + + @Test + void jackson_modelCapabilitiesOverrideSupportsClearedFieldsOmitted() throws Exception { + var supports = new ModelCapabilitiesOverride.Supports(); + supports.setVision(true); + supports.setReasoningEffort(false); + + String withFields = MAPPER.writeValueAsString(supports); + assertTrue(withFields.contains("vision")); + assertTrue(withFields.contains("reasoningEffort")); + + supports.clearVision(); + supports.clearReasoningEffort(); + + String cleared = MAPPER.writeValueAsString(supports); + assertFalse(cleared.contains("vision")); + assertFalse(cleared.contains("reasoningEffort")); + } + + @Test + void jackson_modelCapabilitiesOverrideLimitsClearedFieldsOmitted() throws Exception { + var limits = new ModelCapabilitiesOverride.Limits(); + limits.setMaxPromptTokens(2048); + limits.setMaxOutputTokens(512); + limits.setMaxContextWindowTokens(16384); + + String withFields = MAPPER.writeValueAsString(limits); + assertTrue(withFields.contains("max_prompt_tokens")); + assertTrue(withFields.contains("max_output_tokens")); + assertTrue(withFields.contains("max_context_window_tokens")); + + limits.clearMaxPromptTokens(); + limits.clearMaxOutputTokens(); + limits.clearMaxContextWindowTokens(); + + String cleared = MAPPER.writeValueAsString(limits); + assertFalse(cleared.contains("max_prompt_tokens")); + assertFalse(cleared.contains("max_output_tokens")); + assertFalse(cleared.contains("max_context_window_tokens")); + } +} diff --git a/src/test/java/com/github/copilot/sdk/ProviderConfigTest.java b/src/test/java/com/github/copilot/sdk/ProviderConfigTest.java index e7602619ca..6bbb3ae284 100644 --- a/src/test/java/com/github/copilot/sdk/ProviderConfigTest.java +++ b/src/test/java/com/github/copilot/sdk/ProviderConfigTest.java @@ -410,8 +410,8 @@ void testProviderModelIdAndWireModelSerialization() throws Exception { ProviderConfig deserialized = MAPPER.readValue(MAPPER.writeValueAsString(provider), ProviderConfig.class); assertEquals("gpt-4o", deserialized.getModelId()); assertEquals("my-finetune-v3", deserialized.getWireModel()); - assertEquals(100_000, deserialized.getMaxPromptTokens()); - assertEquals(4096, deserialized.getMaxOutputTokens()); + assertEquals(100_000, deserialized.getMaxPromptTokens().getAsInt()); + assertEquals(4096, deserialized.getMaxOutputTokens().getAsInt()); } @Test @@ -419,8 +419,8 @@ void testProviderModelFieldsDefaultToNull() { var provider = new ProviderConfig(); assertNull(provider.getModelId()); assertNull(provider.getWireModel()); - assertNull(provider.getMaxPromptTokens()); - assertNull(provider.getMaxOutputTokens()); + assertTrue(provider.getMaxPromptTokens().isEmpty()); + assertTrue(provider.getMaxOutputTokens().isEmpty()); } @Test diff --git a/src/test/java/com/github/copilot/sdk/RemoteSessionTest.java b/src/test/java/com/github/copilot/sdk/RemoteSessionTest.java new file mode 100644 index 0000000000..6e093db6ca --- /dev/null +++ b/src/test/java/com/github/copilot/sdk/RemoteSessionTest.java @@ -0,0 +1,399 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------------------------------------------*/ + +package com.github.copilot.sdk; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.copilot.sdk.json.CreateSessionRequest; +import com.github.copilot.sdk.json.ResumeSessionConfig; +import com.github.copilot.sdk.json.ResumeSessionRequest; +import com.github.copilot.sdk.json.SessionConfig; + +/** + * Tests for the {@code remoteSession} feature across all session config types. + *

+ * Validates the complete lifecycle of the remote session mode: + *

    + *
  • Getter/setter and fluent chaining on {@link SessionConfig} and + * {@link ResumeSessionConfig}
  • + *
  • Propagation through {@link SessionRequestBuilder} into + * {@link CreateSessionRequest} and {@link ResumeSessionRequest}
  • + *
  • JSON wire-format serialization: correct key, correct value, omission when + * unset
  • + *
  • Defensive copy via {@code copy()} preserves the value
  • + *
  • All three supported mode values ("off", "export", "on") are transmitted + * correctly
  • + *
+ */ +class RemoteSessionTest { + + private static final ObjectMapper MAPPER = JsonRpcClient.getObjectMapper(); + + // ========================================================================= + // SessionConfig getter/setter/copy + // ========================================================================= + + @Test + void sessionConfig_remoteSessionDefaultsToNull() { + var cfg = new SessionConfig(); + assertNull(cfg.getRemoteSession(), "remoteSession should be null when not set"); + } + + @ParameterizedTest + @ValueSource(strings = {"off", "export", "on"}) + void sessionConfig_setRemoteSessionReturnsSelf(String mode) { + var cfg = new SessionConfig(); + SessionConfig result = cfg.setRemoteSession(mode); + assertSame(cfg, result, "setRemoteSession should return the same instance for chaining"); + assertEquals(mode, cfg.getRemoteSession()); + } + + @Test + void sessionConfig_copyPreservesRemoteSession() { + var original = new SessionConfig().setRemoteSession("export"); + var copy = original.clone(); + assertEquals("export", copy.getRemoteSession()); + } + + @Test + void sessionConfig_copyPreservesNullRemoteSession() { + var original = new SessionConfig(); + var copy = original.clone(); + assertNull(copy.getRemoteSession()); + } + + @Test + void sessionConfig_setRemoteSessionToNullClearsValue() { + var cfg = new SessionConfig().setRemoteSession("on"); + cfg.setRemoteSession(null); + assertNull(cfg.getRemoteSession()); + } + + // ========================================================================= + // ResumeSessionConfig getter/setter/copy + // ========================================================================= + + @Test + void resumeSessionConfig_remoteSessionDefaultsToNull() { + var cfg = new ResumeSessionConfig(); + assertNull(cfg.getRemoteSession(), "remoteSession should be null when not set"); + } + + @ParameterizedTest + @ValueSource(strings = {"off", "export", "on"}) + void resumeSessionConfig_setRemoteSessionReturnsSelf(String mode) { + var cfg = new ResumeSessionConfig(); + ResumeSessionConfig result = cfg.setRemoteSession(mode); + assertSame(cfg, result, "setRemoteSession should return the same instance for chaining"); + assertEquals(mode, cfg.getRemoteSession()); + } + + @Test + void resumeSessionConfig_copyPreservesRemoteSession() { + var original = new ResumeSessionConfig().setRemoteSession("on"); + var copy = original.clone(); + assertEquals("on", copy.getRemoteSession()); + } + + // ========================================================================= + // SessionRequestBuilder – CreateSessionRequest wiring + // ========================================================================= + + @ParameterizedTest + @ValueSource(strings = {"off", "export", "on"}) + void buildCreateRequest_propagatesRemoteSession(String mode) { + var config = new SessionConfig().setRemoteSession(mode); + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + assertEquals(mode, request.getRemoteSession()); + } + + @Test + void buildCreateRequest_nullConfig_remoteSessionIsNull() { + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(null); + assertNull(request.getRemoteSession()); + } + + @Test + void buildCreateRequest_unsetRemoteSession_isNull() { + var config = new SessionConfig(); + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + assertNull(request.getRemoteSession()); + } + + // ========================================================================= + // SessionRequestBuilder – ResumeSessionRequest wiring + // ========================================================================= + + @ParameterizedTest + @ValueSource(strings = {"off", "export", "on"}) + void buildResumeRequest_propagatesRemoteSession(String mode) { + var config = new ResumeSessionConfig().setRemoteSession(mode); + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config); + assertEquals(mode, request.getRemoteSession()); + } + + @Test + void buildResumeRequest_nullConfig_remoteSessionIsNull() { + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", null); + assertNull(request.getRemoteSession()); + } + + @Test + void buildResumeRequest_unsetRemoteSession_isNull() { + var config = new ResumeSessionConfig(); + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config); + assertNull(request.getRemoteSession()); + } + + // ========================================================================= + // JSON wire-format: CreateSessionRequest + // ========================================================================= + + @ParameterizedTest + @ValueSource(strings = {"off", "export", "on"}) + void createRequest_serializesRemoteSessionCorrectly(String mode) throws Exception { + var config = new SessionConfig().setRemoteSession(mode); + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + + String json = MAPPER.writeValueAsString(request); + JsonNode tree = MAPPER.readTree(json); + + assertTrue(tree.has("remoteSession"), "Serialized JSON should contain 'remoteSession' field for mode: " + mode); + assertEquals(mode, tree.get("remoteSession").asText()); + } + + @Test + void createRequest_omitsRemoteSessionWhenNull() throws Exception { + var config = new SessionConfig(); + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + + String json = MAPPER.writeValueAsString(request); + JsonNode tree = MAPPER.readTree(json); + + assertFalse(tree.has("remoteSession"), "Serialized JSON should omit 'remoteSession' when not set"); + } + + // ========================================================================= + // JSON wire-format: ResumeSessionRequest + // ========================================================================= + + @ParameterizedTest + @ValueSource(strings = {"off", "export", "on"}) + void resumeRequest_serializesRemoteSessionCorrectly(String mode) throws Exception { + var config = new ResumeSessionConfig().setRemoteSession(mode); + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config); + + String json = MAPPER.writeValueAsString(request); + JsonNode tree = MAPPER.readTree(json); + + assertTrue(tree.has("remoteSession"), "Serialized JSON should contain 'remoteSession' field for mode: " + mode); + assertEquals(mode, tree.get("remoteSession").asText()); + } + + @Test + void resumeRequest_omitsRemoteSessionWhenNull() throws Exception { + var config = new ResumeSessionConfig(); + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config); + + String json = MAPPER.writeValueAsString(request); + JsonNode tree = MAPPER.readTree(json); + + assertFalse(tree.has("remoteSession"), "Serialized JSON should omit 'remoteSession' when not set"); + } + + // ========================================================================= + // JSON round-trip: CreateSessionRequest + // ========================================================================= + + @ParameterizedTest + @ValueSource(strings = {"off", "export", "on"}) + void createRequest_roundTripsRemoteSession(String mode) throws Exception { + var config = new SessionConfig().setRemoteSession(mode); + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + + String json = MAPPER.writeValueAsString(request); + CreateSessionRequest deserialized = MAPPER.readValue(json, CreateSessionRequest.class); + assertEquals(mode, deserialized.getRemoteSession()); + } + + @Test + void createRequest_roundTripsNullRemoteSession() throws Exception { + var config = new SessionConfig(); + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + + String json = MAPPER.writeValueAsString(request); + CreateSessionRequest deserialized = MAPPER.readValue(json, CreateSessionRequest.class); + assertNull(deserialized.getRemoteSession()); + } + + // ========================================================================= + // JSON round-trip: ResumeSessionRequest + // ========================================================================= + + @ParameterizedTest + @ValueSource(strings = {"off", "export", "on"}) + void resumeRequest_roundTripsRemoteSession(String mode) throws Exception { + var config = new ResumeSessionConfig().setRemoteSession(mode); + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config); + + String json = MAPPER.writeValueAsString(request); + ResumeSessionRequest deserialized = MAPPER.readValue(json, ResumeSessionRequest.class); + assertEquals(mode, deserialized.getRemoteSession()); + } + + // ========================================================================= + // Fluent chaining: remoteSession composes with other config options + // ========================================================================= + + @Test + void sessionConfig_remoteSessionComposesWithOtherFields() { + var config = new SessionConfig().setModel("gpt-4o").setRemoteSession("export").setReasoningEffort("high"); + + assertEquals("gpt-4o", config.getModel()); + assertEquals("export", config.getRemoteSession()); + assertEquals("high", config.getReasoningEffort()); + } + + @Test + void resumeSessionConfig_remoteSessionComposesWithOtherFields() { + var config = new ResumeSessionConfig().setModel("gpt-4o").setRemoteSession("on").setReasoningEffort("medium"); + + assertEquals("gpt-4o", config.getModel()); + assertEquals("on", config.getRemoteSession()); + assertEquals("medium", config.getReasoningEffort()); + } + + @Test + void createRequest_remoteSessionDoesNotAffectOtherFields() throws Exception { + var config = new SessionConfig().setModel("gpt-4o").setRemoteSession("export").setReasoningEffort("high") + .setGitHubToken("ghp_test"); + + CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config); + String json = MAPPER.writeValueAsString(request); + JsonNode tree = MAPPER.readTree(json); + + assertEquals("export", tree.get("remoteSession").asText()); + assertEquals("gpt-4o", tree.get("model").asText()); + assertEquals("high", tree.get("reasoningEffort").asText()); + assertEquals("ghp_test", tree.get("gitHubToken").asText()); + } + + @Test + void resumeRequest_remoteSessionDoesNotAffectOtherFields() throws Exception { + var config = new ResumeSessionConfig().setModel("gpt-4o").setRemoteSession("on").setReasoningEffort("medium") + .setGitHubToken("ghp_test"); + + ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("sid-1", config); + String json = MAPPER.writeValueAsString(request); + JsonNode tree = MAPPER.readTree(json); + + assertEquals("on", tree.get("remoteSession").asText()); + assertEquals("gpt-4o", tree.get("model").asText()); + assertEquals("medium", tree.get("reasoningEffort").asText()); + assertEquals("ghp_test", tree.get("gitHubToken").asText()); + } + + // ========================================================================= + // Deserialization from raw JSON (simulates CLI response ingestion) + // ========================================================================= + + @Test + void createRequest_deserializesRemoteSessionFromRawJson() throws Exception { + String json = """ + { + "sessionId": "test-session", + "remoteSession": "export", + "model": "gpt-4o" + } + """; + CreateSessionRequest request = MAPPER.readValue(json, CreateSessionRequest.class); + assertEquals("export", request.getRemoteSession()); + assertEquals("test-session", request.getSessionId()); + } + + @Test + void resumeRequest_deserializesRemoteSessionFromRawJson() throws Exception { + String json = """ + { + "sessionId": "resume-session", + "remoteSession": "on", + "model": "gpt-4o" + } + """; + ResumeSessionRequest request = MAPPER.readValue(json, ResumeSessionRequest.class); + assertEquals("on", request.getRemoteSession()); + assertEquals("resume-session", request.getSessionId()); + } + + @Test + void createRequest_deserializesWithMissingRemoteSession() throws Exception { + String json = """ + { + "sessionId": "test-session", + "model": "gpt-4o" + } + """; + CreateSessionRequest request = MAPPER.readValue(json, CreateSessionRequest.class); + assertNull(request.getRemoteSession()); + } + + // ========================================================================= + // Handoff event with remoteSessionId (remote session lifecycle) + // ========================================================================= + + @Test + void handoffEvent_withRemoteSourceType_containsRemoteSessionId() throws Exception { + String json = """ + { + "type": "session.handoff", + "data": { + "handoffTime": "2025-06-01T12:00:00Z", + "sourceType": "remote", + "remoteSessionId": "remote-sess-42", + "summary": "Session exported for remote execution", + "repository": { + "owner": "test-org", + "name": "test-repo", + "branch": "feature-branch" + } + } + } + """; + + var event = (com.github.copilot.sdk.generated.SessionHandoffEvent) MAPPER.readValue(json, + com.github.copilot.sdk.generated.SessionEvent.class); + assertNotNull(event); + var data = event.getData(); + assertEquals("remote-sess-42", data.remoteSessionId()); + assertEquals(com.github.copilot.sdk.generated.HandoffSourceType.REMOTE, data.sourceType()); + assertEquals("Session exported for remote execution", data.summary()); + assertEquals("test-org", data.repository().owner()); + assertEquals("test-repo", data.repository().name()); + assertEquals("feature-branch", data.repository().branch()); + } + + @Test + void handoffEvent_withoutRemoteSessionId_fieldIsNull() throws Exception { + String json = """ + { + "type": "session.handoff", + "data": { + "targetAgent": "local-agent" + } + } + """; + + var event = (com.github.copilot.sdk.generated.SessionHandoffEvent) MAPPER.readValue(json, + com.github.copilot.sdk.generated.SessionEvent.class); + assertNotNull(event); + assertNull(event.getData().remoteSessionId()); + } +} diff --git a/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java b/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java index 99b360d2db..278777ce13 100644 --- a/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java +++ b/src/test/java/com/github/copilot/sdk/TelemetryConfigTest.java @@ -22,7 +22,7 @@ void defaultValuesAreNull() { assertNull(config.getFilePath()); assertNull(config.getExporterType()); assertNull(config.getSourceName()); - assertNull(config.getCaptureContent()); + assertTrue(config.getCaptureContent().isEmpty()); } @Test @@ -57,10 +57,10 @@ void sourceNameGetterSetter() { void captureContentGetterSetter() { var config = new TelemetryConfig(); config.setCaptureContent(true); - assertTrue(config.getCaptureContent()); + assertTrue(config.getCaptureContent().get()); config.setCaptureContent(false); - assertFalse(config.getCaptureContent()); + assertFalse(config.getCaptureContent().get()); } @Test @@ -72,6 +72,6 @@ void fluentChainingReturnsThis() { assertEquals("/tmp/spans.json", config.getFilePath()); assertEquals("file", config.getExporterType()); assertEquals("sdk-test", config.getSourceName()); - assertTrue(config.getCaptureContent()); + assertTrue(config.getCaptureContent().get()); } } diff --git a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java index 89943e3758..10393abe09 100644 --- a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java +++ b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcApiCoverageTest.java @@ -71,7 +71,7 @@ void serverRpc_sessions_fork_invokes_correct_method() { var stub = new StubCaller(); var server = new ServerRpc(stub); - var params = new SessionsForkParams("parent-session-id", null); + var params = new SessionsForkParams("parent-session-id", null, null); server.sessions.fork(params); assertEquals(1, stub.calls.size()); @@ -657,7 +657,7 @@ void serverRpc_sessionFs_setProvider_params_record() { @Test void sessionsForkParams_record() { - var params = new SessionsForkParams("parent-id", "event-123"); + var params = new SessionsForkParams("parent-id", "event-123", null); assertEquals("parent-id", params.sessionId()); assertEquals("event-123", params.toEventId()); } diff --git a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java index 48ade8ce11..50e65d3779 100644 --- a/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java +++ b/src/test/java/com/github/copilot/sdk/generated/rpc/GeneratedRpcRecordsCoverageTest.java @@ -67,7 +67,7 @@ void toolsListParams_record() { @Test void sessionsForkParams_record() { - var params = new SessionsForkParams("sess-1", "event-abc"); + var params = new SessionsForkParams("sess-1", "event-abc", null); assertEquals("sess-1", params.sessionId()); assertEquals("event-abc", params.toEventId()); } @@ -795,7 +795,7 @@ void sessionSkillsListResult_nested() { @Test void sessionSkillsReloadResult_empty() { - assertNotNull(new SessionSkillsReloadResult()); + assertNotNull(new SessionSkillsReloadResult(null, null)); } @Test @@ -863,7 +863,7 @@ void sessionWorkspaceReadFileResult_record() { @Test void sessionsForkResult_record() { - var result = new SessionsForkResult("forked-sess-id"); + var result = new SessionsForkResult("forked-sess-id", null); assertEquals("forked-sess-id", result.sessionId()); } @@ -917,8 +917,8 @@ void modelsListResult_nested() { var limits = new ModelCapabilitiesLimits(100000L, 8192L, 128000L, null); var capabilities = new ModelCapabilities(supports, limits); var policy = new ModelPolicy("active", null); - var billing = new ModelBilling(1.0); - var modelItem = new Model("gpt-5", "GPT-5", capabilities, policy, billing, null, null); + var billing = new ModelBilling(1.0, null); + var modelItem = new Model("gpt-5", "GPT-5", capabilities, policy, billing, null, null, null, null); var result = new ModelsListResult(List.of(modelItem)); assertEquals(1, result.models().size());