From bfd2d979ff1f9f4ddcc031e109554e6629b63bb9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 8 May 2026 22:24:04 +0000
Subject: [PATCH 01/18] Initial plan
From 0b5c422720c2fbf7cdf920ec0a10e0cf853ac3b5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 8 May 2026 22:26:50 +0000
Subject: [PATCH 02/18] Fix codegen-agentic-fix workflow: remove labels
requirement for push-to-pull-request-branch
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
---
.../workflows/codegen-agentic-fix.lock.yml | 30 +++++++++----------
.github/workflows/codegen-agentic-fix.md | 1 -
2 files changed, 15 insertions(+), 16 deletions(-)
diff --git a/.github/workflows/codegen-agentic-fix.lock.yml b/.github/workflows/codegen-agentic-fix.lock.yml
index 958f3cd155..9a75a41537 100644
--- a/.github/workflows/codegen-agentic-fix.lock.yml
+++ b/.github/workflows/codegen-agentic-fix.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"70880e5fb9ec78342cf56974b2414d8cf0a516533e201e437835164543ef58c0","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"}
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"22995cb1fca1a0e3af19ac8bfc2ab7a114e72bb33cd7bf180b818a28682678ae","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"ba90f2186d7ad780ec640f364005fa24e797b360","version":"v0.68.3"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0"},{"image":"node:lts-alpine"}]}
# ___ _ _
# / _ \ | | (_)
@@ -178,19 +178,19 @@ jobs:
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_d94a435f1fbdc38f_EOF'
+ cat << 'GH_AW_PROMPT_0adaf9d66984c7fc_EOF'
- GH_AW_PROMPT_d94a435f1fbdc38f_EOF
+ GH_AW_PROMPT_0adaf9d66984c7fc_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_d94a435f1fbdc38f_EOF'
+ cat << 'GH_AW_PROMPT_0adaf9d66984c7fc_EOF'
Tools: add_comment(max:5), push_to_pull_request_branch, missing_tool, missing_data, noop
- GH_AW_PROMPT_d94a435f1fbdc38f_EOF
+ GH_AW_PROMPT_0adaf9d66984c7fc_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_push_to_pr_branch.md"
- cat << 'GH_AW_PROMPT_d94a435f1fbdc38f_EOF'
+ cat << 'GH_AW_PROMPT_0adaf9d66984c7fc_EOF'
The following GitHub context information is available for this workflow:
@@ -220,12 +220,12 @@ jobs:
{{/if}}
- GH_AW_PROMPT_d94a435f1fbdc38f_EOF
+ GH_AW_PROMPT_0adaf9d66984c7fc_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_d94a435f1fbdc38f_EOF'
+ cat << 'GH_AW_PROMPT_0adaf9d66984c7fc_EOF'
{{#runtime-import .github/workflows/codegen-agentic-fix.md}}
- GH_AW_PROMPT_d94a435f1fbdc38f_EOF
+ GH_AW_PROMPT_0adaf9d66984c7fc_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
@@ -402,9 +402,9 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_b6e7bb90508de076_EOF'
- {"add_comment":{"max":5,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"push_to_pull_request_branch":{"if_no_changes":"warn","labels":["dependencies"],"max_patch_size":1024,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS"],"protected_path_prefixes":[".github/",".agents/"],"target":"*"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_b6e7bb90508de076_EOF
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_3142db99d39a4285_EOF'
+ {"add_comment":{"max":5,"target":"*"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"push_to_pull_request_branch":{"if_no_changes":"warn","max_patch_size":1024,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS"],"protected_path_prefixes":[".github/",".agents/"],"target":"*"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_3142db99d39a4285_EOF
- name: Write Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
@@ -608,7 +608,7 @@ jobs:
export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.2.19'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_f8f308e27510535f_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh"
+ cat << GH_AW_MCP_CONFIG_47ddd2dcd9c136d7_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh"
{
"mcpServers": {
"github": {
@@ -649,7 +649,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_f8f308e27510535f_EOF
+ GH_AW_MCP_CONFIG_47ddd2dcd9c136d7_EOF
- name: Download activation artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
@@ -1232,7 +1232,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":5,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"labels\":[\"dependencies\"],\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"target\":\"*\"},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":5,\"target\":\"*\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"target\":\"*\"},\"report_incomplete\":{}}"
GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/codegen-agentic-fix.md b/.github/workflows/codegen-agentic-fix.md
index b4aa0a8f20..ea6913f8d3 100644
--- a/.github/workflows/codegen-agentic-fix.md
+++ b/.github/workflows/codegen-agentic-fix.md
@@ -37,7 +37,6 @@ tools:
safe-outputs:
push-to-pull-request-branch:
target: "*"
- labels: [dependencies]
add-comment:
target: "*"
max: 5
From 0984ed0faab4944b27dd96fa6f0a4c9f71f63a48 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Fri, 8 May 2026 23:39:27 +0000
Subject: [PATCH 03/18] [maven-release-plugin] prepare for next development
iteration
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 351b510103..832f2f2c9d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
com.githubcopilot-sdk-java
- 1.0.0-beta-java.2
+ 1.0.0-beta-java.3-SNAPSHOTjarGitHub Copilot SDK :: Java
@@ -33,7 +33,7 @@
scm:git:https://github.com/github/copilot-sdk-java.gitscm:git:https://github.com/github/copilot-sdk-java.githttps://github.com/github/copilot-sdk-java
- v1.0.0-beta-java.2
+ HEAD
From a8dcb5f8cd9b45d7d9ceb3f69741568dace7761b Mon Sep 17 00:00:00 2001
From: brunoborges <129743+brunoborges@users.noreply.github.com>
Date: Sun, 10 May 2026 00:51:57 +0000
Subject: [PATCH 04/18] Update JaCoCo coverage badge
---
.github/badges/jacoco.svg | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg
index 777a7e53a4..def483aed2 100644
--- a/.github/badges/jacoco.svg
+++ b/.github/badges/jacoco.svg
@@ -12,7 +12,7 @@
coveragecoverage
- 80.8%
- 80.8%
+ 81.4%
+ 81.4%
From 5345510572fe08f7728012c1866e85a549964157 Mon Sep 17 00:00:00 2001
From: Ed Burns
Date: Mon, 11 May 2026 13:36:22 -0400
Subject: [PATCH 05/18] Close stale sync PRs in reference-impl-sync workflow
- Delete redundant non-agentic reference-impl-sync.yml workflow
- Add close-pull-request safe-output to reference-impl-sync.md
- Add pull_requests toolset and pull-requests:read permission
- Recompile lock file
---
.../workflows/reference-impl-sync.lock.yml | 50 +++--
.github/workflows/reference-impl-sync.md | 16 +-
.github/workflows/reference-impl-sync.yml | 181 ------------------
3 files changed, 46 insertions(+), 201 deletions(-)
delete mode 100644 .github/workflows/reference-impl-sync.yml
diff --git a/.github/workflows/reference-impl-sync.lock.yml b/.github/workflows/reference-impl-sync.lock.yml
index 5cd565b426..50ed7d8873 100644
--- a/.github/workflows/reference-impl-sync.lock.yml
+++ b/.github/workflows/reference-impl-sync.lock.yml
@@ -1,4 +1,4 @@
-# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"e8c3534f6cb36bd08b5436922ff89339bc22c719895422c0526b8a7779186327","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"}
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"54b1896209b363ceadbcb123b6c46dbbcc0118d9140313360c341b09d03bbb96","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_AGENT_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"ba90f2186d7ad780ec640f364005fa24e797b360","version":"v0.68.3"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0"},{"image":"node:lts-alpine"}]}
# ___ _ _
# / _ \ | | (_)
@@ -166,16 +166,16 @@ jobs:
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_8be4e91e77b39cd4_EOF'
+ cat << 'GH_AW_PROMPT_c9d6e485c3f68bea_EOF'
- GH_AW_PROMPT_8be4e91e77b39cd4_EOF
+ GH_AW_PROMPT_c9d6e485c3f68bea_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_8be4e91e77b39cd4_EOF'
+ cat << 'GH_AW_PROMPT_c9d6e485c3f68bea_EOF'
- Tools: add_comment(max:10), create_issue, close_issue(max:10), assign_to_agent, missing_tool, missing_data, noop
+ Tools: add_comment(max:10), create_issue, close_issue(max:10), close_pull_request(max:10), assign_to_agent, missing_tool, missing_data, noop
The following GitHub context information is available for this workflow:
@@ -205,12 +205,12 @@ jobs:
{{/if}}
- GH_AW_PROMPT_8be4e91e77b39cd4_EOF
+ GH_AW_PROMPT_c9d6e485c3f68bea_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_8be4e91e77b39cd4_EOF'
+ cat << 'GH_AW_PROMPT_c9d6e485c3f68bea_EOF'
{{#runtime-import .github/workflows/reference-impl-sync.md}}
- GH_AW_PROMPT_8be4e91e77b39cd4_EOF
+ GH_AW_PROMPT_c9d6e485c3f68bea_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
@@ -284,6 +284,7 @@ jobs:
actions: read
contents: read
issues: read
+ pull-requests: read
concurrency:
group: "gh-aw-copilot-${{ github.workflow }}"
env:
@@ -381,9 +382,9 @@ jobs:
mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_76ba061ad4465f81_EOF'
- {"add_comment":{"max":10,"target":"*"},"assign_to_agent":{"max":1,"model":"claude-opus-4.6","name":"copilot","target":"*"},"close_issue":{"max":10,"required_labels":["reference-impl-sync"],"target":"*"},"create_issue":{"expires":144,"labels":["reference-impl-sync"],"max":1,"title_prefix":"[reference-impl-sync] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_76ba061ad4465f81_EOF
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_06a3a494957bf1f1_EOF'
+ {"add_comment":{"max":10,"target":"*"},"assign_to_agent":{"max":1,"model":"claude-opus-4.6","name":"copilot","target":"*"},"close_issue":{"max":10,"required_labels":["reference-impl-sync"],"target":"*"},"close_pull_request":{"max":10,"target":"*"},"create_issue":{"expires":144,"labels":["reference-impl-sync"],"max":1,"title_prefix":"[reference-impl-sync] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_06a3a494957bf1f1_EOF
- name: Write Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
@@ -392,6 +393,7 @@ jobs:
"add_comment": " CONSTRAINTS: Maximum 10 comment(s) can be added. Target: *. Supports reply_to_id for discussion threading.",
"assign_to_agent": " CONSTRAINTS: Maximum 1 issue(s) can be assigned to agent.",
"close_issue": " CONSTRAINTS: Maximum 10 issue(s) can be closed. Target: *.",
+ "close_pull_request": " CONSTRAINTS: Maximum 10 pull request(s) can be closed. Target: *.",
"create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[reference-impl-sync] \". Labels [\"reference-impl-sync\"] will be automatically added."
},
"repo_params": {},
@@ -464,6 +466,24 @@ jobs:
}
}
},
+ "close_pull_request": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "pull_request_number": {
+ "optionalPositiveInteger": true
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ }
+ }
+ },
"create_issue": {
"defaultMax": 1,
"fields": {
@@ -646,7 +666,7 @@ jobs:
export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.2.19'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_cf52d533554e8317_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh"
+ cat << GH_AW_MCP_CONFIG_03f014fbed5a3560_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh"
{
"mcpServers": {
"github": {
@@ -656,7 +676,7 @@ jobs:
"GITHUB_HOST": "\${GITHUB_SERVER_URL}",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
- "GITHUB_TOOLSETS": "context,repos,issues"
+ "GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
},
"guard-policies": {
"allow-only": {
@@ -687,7 +707,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_cf52d533554e8317_EOF
+ GH_AW_MCP_CONFIG_03f014fbed5a3560_EOF
- name: Download activation artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
@@ -1245,7 +1265,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"assign_to_agent\":{\"max\":1,\"model\":\"claude-opus-4.6\",\"name\":\"copilot\",\"target\":\"*\"},\"close_issue\":{\"max\":10,\"required_labels\":[\"reference-impl-sync\"],\"target\":\"*\"},\"create_issue\":{\"expires\":144,\"labels\":[\"reference-impl-sync\"],\"max\":1,\"title_prefix\":\"[reference-impl-sync] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"assign_to_agent\":{\"max\":1,\"model\":\"claude-opus-4.6\",\"name\":\"copilot\",\"target\":\"*\"},\"close_issue\":{\"max\":10,\"required_labels\":[\"reference-impl-sync\"],\"target\":\"*\"},\"close_pull_request\":{\"max\":10,\"target\":\"*\"},\"create_issue\":{\"expires\":144,\"labels\":[\"reference-impl-sync\"],\"max\":1,\"title_prefix\":\"[reference-impl-sync] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
GH_AW_ASSIGN_TO_AGENT_TOKEN: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/reference-impl-sync.md b/.github/workflows/reference-impl-sync.md
index 8e77f1a187..20cad89e14 100644
--- a/.github/workflows/reference-impl-sync.md
+++ b/.github/workflows/reference-impl-sync.md
@@ -11,6 +11,7 @@ permissions:
contents: read
actions: read
issues: read
+ pull-requests: read
network:
allowed:
@@ -19,7 +20,7 @@ network:
tools:
github:
- toolsets: [context, repos, issues]
+ toolsets: [context, repos, issues, pull_requests]
safe-outputs:
create-issue:
@@ -37,6 +38,9 @@ safe-outputs:
name: "copilot"
model: "claude-opus-4.6"
target: "*"
+ close-pull-request:
+ target: "*"
+ max: 10
noop:
report-as-issue: false
---
@@ -78,14 +82,16 @@ Go to Step 3b.
1. Search for any open issues with the `reference-impl-sync` label using the GitHub MCP tools.
2. If there are open `reference-impl-sync` labeled issues, close each one using the `close_issue` safe-output tool with a comment: "No new reference implementation changes detected. The Java SDK is up to date. Closing."
-3. Call the `noop` safe-output tool with message: "No new reference implementation changes since last merge ()."
-4. **Stop here.** Do not proceed further.
+3. Search for any open pull requests whose head branch starts with `copilot/reference-impl`. Close each one using the `close_pull_request` safe-output tool with a comment: "Superseded — the Java SDK is up to date with the reference implementation. Closing stale sync PR."
+4. Call the `noop` safe-output tool with message: "No new reference implementation changes since last merge ()."
+5. **Stop here.** Do not proceed further.
### Step 3b: Changes detected
1. Search for any open issues with the `reference-impl-sync` label using the GitHub MCP tools.
2. Close each existing open `reference-impl-sync` issue using the `close_issue` safe-output tool with a comment: "Superseded by a newer reference implementation sync check."
-3. Create a new issue using the `create_issue` safe-output tool with:
+3. Search for any open pull requests whose head branch starts with `copilot/reference-impl`. Close each one using the `close_pull_request` safe-output tool with a comment: "Superseded by a newer reference implementation sync issue. Closing stale sync PR."
+4. Create a new issue using the `create_issue` safe-output tool with:
- **Title:** `Reference Implementation sync: new commits ()`
- **Body:** Include the following information:
```
@@ -112,7 +118,7 @@ Go to Step 3b.
❌❌Do NOT update .lastmerge manually — the finish script does this.❌❌
❌❌Do NOT skip the finish script — it syncs codegen versions and updates .lastmerge.❌❌
```
-4. After creating the issue, use the `assign_to_agent` safe-output tool to assign Copilot to the newly created issue.
+5. After creating the issue, use the `assign_to_agent` safe-output tool to assign Copilot to the newly created issue.
## Important constraints
diff --git a/.github/workflows/reference-impl-sync.yml b/.github/workflows/reference-impl-sync.yml
deleted file mode 100644
index ab8225a2af..0000000000
--- a/.github/workflows/reference-impl-sync.yml
+++ /dev/null
@@ -1,181 +0,0 @@
-name: "Reference Implementation Sync"
-
-on:
- schedule:
- # Tuesday and Thursday at 10:00 UTC (5:00 AM EST / 6:00 AM EDT)
- - cron: '0 10 * * 2,4'
- workflow_dispatch:
-
-permissions:
- contents: write
- issues: write
- pull-requests: write
-
-env:
- # Used for `gh` CLI operations to create/close/comment issues. Must be a PAT with repo permissions because the default
- GH_AW_AGENT_TOKEN: ${{ secrets.GH_AW_AGENT_TOKEN }}
- GH_TOKEN: ${{ secrets.GH_AW_AGENT_TOKEN }}
-
-jobs:
-
- check-and-sync:
- name: "Check reference implementation & trigger Copilot merge"
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
-
- - name: Check for reference implementation changes
- id: check
- run: |
- LAST_MERGE=$(cat .lastmerge)
- echo "Last merged commit: $LAST_MERGE"
-
- git clone --quiet https://github.com/github/copilot-sdk.git /tmp/reference-impl
- cd /tmp/reference-impl
-
- REFERENCE_IMPL_HEAD=$(git rev-parse HEAD)
- echo "Reference implementation HEAD: $REFERENCE_IMPL_HEAD"
-
- echo "last_merge=$LAST_MERGE" >> "$GITHUB_OUTPUT"
-
- if [ "$LAST_MERGE" = "$REFERENCE_IMPL_HEAD" ]; then
- echo "No new reference implementation changes since last merge."
- echo "has_changes=false" >> "$GITHUB_OUTPUT"
- else
- COMMIT_COUNT=$(git rev-list --count "$LAST_MERGE".."$REFERENCE_IMPL_HEAD")
- echo "Found $COMMIT_COUNT new reference implementation commits."
- echo "has_changes=true" >> "$GITHUB_OUTPUT"
- echo "commit_count=$COMMIT_COUNT" >> "$GITHUB_OUTPUT"
- echo "reference_impl_head=$REFERENCE_IMPL_HEAD" >> "$GITHUB_OUTPUT"
-
- # Generate a short summary of changes
- SUMMARY=$(git log --oneline "$LAST_MERGE".."$REFERENCE_IMPL_HEAD" | head -20)
- {
- echo "summary<> "$GITHUB_OUTPUT"
- fi
-
- - name: Close previous reference-impl-sync issues
- if: steps.check.outputs.has_changes == 'true'
- run: |
- # Find all open issues with the reference-impl-sync label
- OPEN_ISSUES=$(gh issue list \
- --repo "${{ github.repository }}" \
- --label "reference-impl-sync" \
- --state open \
- --json number \
- --jq '.[].number')
-
- for ISSUE_NUM in $OPEN_ISSUES; do
- echo "Closing superseded issue #${ISSUE_NUM}"
- gh issue comment "$ISSUE_NUM" \
- --repo "${{ github.repository }}" \
- --body "Superseded by a newer reference implementation sync issue. Closing this one."
- gh issue close "$ISSUE_NUM" \
- --repo "${{ github.repository }}" \
- --reason "not planned"
- done
-
- - name: Close stale reference-impl-sync issues (no changes)
- if: steps.check.outputs.has_changes == 'false'
- run: |
- OPEN_ISSUES=$(gh issue list \
- --repo "${{ github.repository }}" \
- --label "reference-impl-sync" \
- --state open \
- --json number \
- --jq '.[].number')
-
- for ISSUE_NUM in $OPEN_ISSUES; do
- echo "Closing stale issue #${ISSUE_NUM} — reference implementation is up to date"
- gh issue comment "$ISSUE_NUM" \
- --repo "${{ github.repository }}" \
- --body "No new reference implementation changes detected. The Java SDK is up to date. Closing."
- gh issue close "$ISSUE_NUM" \
- --repo "${{ github.repository }}" \
- --reason "completed"
- done
-
- - name: Create issue and assign to Copilot
- id: create-issue
- if: steps.check.outputs.has_changes == 'true'
- env:
- SUMMARY: ${{ steps.check.outputs.summary }}
- run: |
- COMMIT_COUNT="${{ steps.check.outputs.commit_count }}"
- LAST_MERGE="${{ steps.check.outputs.last_merge }}"
- REFERENCE_IMPL_HEAD="${{ steps.check.outputs.reference_impl_head }}"
- DATE=$(date -u +"%Y-%m-%d")
- REPO="${{ github.repository }}"
-
- BODY="## Automated Reference Implementation Sync
-
- There are **${COMMIT_COUNT}** new commits in the [official Copilot SDK](https://github.com/github/copilot-sdk) since the last merge.
-
- - **Last merged commit:** [\`${LAST_MERGE}\`](https://github.com/github/copilot-sdk/commit/${LAST_MERGE})
- - **Reference implementation HEAD:** [\`${REFERENCE_IMPL_HEAD}\`](https://github.com/github/copilot-sdk/commit/${REFERENCE_IMPL_HEAD})
-
- ### Recent reference implementation commits
-
- \`\`\`
- ${SUMMARY}
- \`\`\`
-
- ### Instructions
-
- Follow the [agentic-merge-reference-impl](.github/prompts/agentic-merge-reference-impl.prompt.md) prompt to port these changes to the Java SDK."
-
- # Create the issue with the label; the Copilot coding agent picks up
- # unassigned issues that carry the configured label.
- ISSUE_URL=$(gh issue create \
- --repo "$REPO" \
- --title "Reference implementation sync: ${COMMIT_COUNT} new commits (${DATE})" \
- --body "$BODY" \
- --label "reference-impl-sync")
-
- echo "issue_url=$ISSUE_URL" >> "$GITHUB_OUTPUT"
- echo "✅ Issue created with label for Copilot coding agent: $ISSUE_URL"
-
- - name: Summary
- if: always()
- env:
- SUMMARY: ${{ steps.check.outputs.summary }}
- run: |
- HAS_CHANGES="${{ steps.check.outputs.has_changes }}"
- COMMIT_COUNT="${{ steps.check.outputs.commit_count }}"
- LAST_MERGE="${{ steps.check.outputs.last_merge }}"
- REFERENCE_IMPL_HEAD="${{ steps.check.outputs.reference_impl_head }}"
- ISSUE_URL="${{ steps.create-issue.outputs.issue_url }}"
-
- {
- echo "## Reference Implementation Sync"
- echo ""
- if [ "$HAS_CHANGES" = "true" ]; then
- echo "### ✅ New reference implementation changes detected"
- echo ""
- echo "| | |"
- echo "|---|---|"
- echo "| **New commits** | ${COMMIT_COUNT} |"
- echo "| **Last merged** | \`${LAST_MERGE:0:12}\` |"
- echo "| **Reference implementation HEAD** | \`${REFERENCE_IMPL_HEAD:0:12}\` |"
- echo ""
- echo "An issue has been created and assigned to the Copilot coding agent: "
- echo " -> ${ISSUE_URL}"
- echo ""
- echo "### Recent reference implementation commits"
- echo ""
- echo '```'
- echo "$SUMMARY"
- echo '```'
- else
- echo "### ⏭️ No changes"
- echo ""
- echo "The Java SDK is already up to date with the reference implementation Copilot SDK."
- echo ""
- echo "**Last merged commit:** \`${LAST_MERGE:0:12}\`"
- fi
- } >> "$GITHUB_STEP_SUMMARY"
From 0614c8f68c32cdf3f29613e53bbc6d51c9494d80 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 11 May 2026 17:41:04 +0000
Subject: [PATCH 06/18] Initial plan
From a23357e5a065c7d5ba9fa4cb2f3184bd6799e74e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 11 May 2026 17:51:40 +0000
Subject: [PATCH 07/18] Port exit-plan-mode and auto-mode-switch handler APIs
from reference implementation
Adds ExitPlanModeHandler and AutoModeSwitchHandler support matching the
reference implementation commit 671b50a (Restore mode handler APIs across SDKs).
New types:
- ExitPlanModeHandler, ExitPlanModeRequest, ExitPlanModeResult, ExitPlanModeInvocation
- AutoModeSwitchHandler, AutoModeSwitchRequest, AutoModeSwitchResponse, AutoModeSwitchInvocation
Updated:
- SessionConfig/ResumeSessionConfig: onExitPlanMode, onAutoModeSwitch fields
- CreateSessionRequest/ResumeSessionRequest: requestExitPlanMode, requestAutoModeSwitch flags
- CopilotSession: handler registration and dispatch methods
- RpcHandlerDispatcher: exitPlanMode.request, autoModeSwitch.request handlers
- SessionRequestBuilder: wiring for new handlers and capability flags
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
---
.../github/copilot/sdk/CopilotSession.java | 92 ++++++++++++++
.../copilot/sdk/RpcHandlerDispatcher.java | 96 ++++++++++++++
.../copilot/sdk/SessionRequestBuilder.java | 24 ++++
.../sdk/json/AutoModeSwitchHandler.java | 48 +++++++
.../sdk/json/AutoModeSwitchInvocation.java | 36 ++++++
.../sdk/json/AutoModeSwitchRequest.java | 68 ++++++++++
.../sdk/json/AutoModeSwitchResponse.java | 40 ++++++
.../sdk/json/CreateSessionRequest.java | 28 +++++
.../copilot/sdk/json/ExitPlanModeHandler.java | 48 +++++++
.../sdk/json/ExitPlanModeInvocation.java | 36 ++++++
.../copilot/sdk/json/ExitPlanModeRequest.java | 119 ++++++++++++++++++
.../copilot/sdk/json/ExitPlanModeResult.java | 87 +++++++++++++
.../copilot/sdk/json/ResumeSessionConfig.java | 58 +++++++++
.../sdk/json/ResumeSessionRequest.java | 28 +++++
.../copilot/sdk/json/SessionConfig.java | 58 +++++++++
15 files changed, 866 insertions(+)
create mode 100644 src/main/java/com/github/copilot/sdk/json/AutoModeSwitchHandler.java
create mode 100644 src/main/java/com/github/copilot/sdk/json/AutoModeSwitchInvocation.java
create mode 100644 src/main/java/com/github/copilot/sdk/json/AutoModeSwitchRequest.java
create mode 100644 src/main/java/com/github/copilot/sdk/json/AutoModeSwitchResponse.java
create mode 100644 src/main/java/com/github/copilot/sdk/json/ExitPlanModeHandler.java
create mode 100644 src/main/java/com/github/copilot/sdk/json/ExitPlanModeInvocation.java
create mode 100644 src/main/java/com/github/copilot/sdk/json/ExitPlanModeRequest.java
create mode 100644 src/main/java/com/github/copilot/sdk/json/ExitPlanModeResult.java
diff --git a/src/main/java/com/github/copilot/sdk/CopilotSession.java b/src/main/java/com/github/copilot/sdk/CopilotSession.java
index e4cb984644..ad5561c09e 100644
--- a/src/main/java/com/github/copilot/sdk/CopilotSession.java
+++ b/src/main/java/com/github/copilot/sdk/CopilotSession.java
@@ -54,6 +54,10 @@
import com.github.copilot.sdk.generated.SessionEvent;
import com.github.copilot.sdk.generated.SessionIdleEvent;
import com.github.copilot.sdk.json.AgentInfo;
+import com.github.copilot.sdk.json.AutoModeSwitchHandler;
+import com.github.copilot.sdk.json.AutoModeSwitchInvocation;
+import com.github.copilot.sdk.json.AutoModeSwitchRequest;
+import com.github.copilot.sdk.json.AutoModeSwitchResponse;
import com.github.copilot.sdk.json.CommandContext;
import com.github.copilot.sdk.json.CommandDefinition;
import com.github.copilot.sdk.json.CommandHandler;
@@ -62,6 +66,10 @@
import com.github.copilot.sdk.json.ElicitationParams;
import com.github.copilot.sdk.json.ElicitationResult;
import com.github.copilot.sdk.json.ElicitationResultAction;
+import com.github.copilot.sdk.json.ExitPlanModeHandler;
+import com.github.copilot.sdk.json.ExitPlanModeInvocation;
+import com.github.copilot.sdk.json.ExitPlanModeRequest;
+import com.github.copilot.sdk.json.ExitPlanModeResult;
import com.github.copilot.sdk.json.ElicitationSchema;
import com.github.copilot.sdk.json.GetMessagesResponse;
import com.github.copilot.sdk.json.HookInvocation;
@@ -156,6 +164,8 @@ public final class CopilotSession implements AutoCloseable {
private final AtomicReference permissionHandler = new AtomicReference<>();
private final AtomicReference userInputHandler = new AtomicReference<>();
private final AtomicReference elicitationHandler = new AtomicReference<>();
+ private final AtomicReference exitPlanModeHandler = new AtomicReference<>();
+ private final AtomicReference autoModeSwitchHandler = new AtomicReference<>();
private final AtomicReference hooksHandler = new AtomicReference<>();
private volatile EventErrorHandler eventErrorHandler;
private volatile EventErrorPolicy eventErrorPolicy = EventErrorPolicy.PROPAGATE_AND_LOG_ERRORS;
@@ -1317,6 +1327,32 @@ void registerElicitationHandler(ElicitationHandler handler) {
elicitationHandler.set(handler);
}
+ /**
+ * Registers an exit-plan-mode handler for this session.
+ *
+ * Called internally when creating or resuming a session with an exit-plan-mode
+ * handler.
+ *
+ * @param handler
+ * the handler to invoke when an exit-plan-mode request is received
+ */
+ void registerExitPlanModeHandler(ExitPlanModeHandler handler) {
+ exitPlanModeHandler.set(handler);
+ }
+
+ /**
+ * Registers an auto-mode-switch handler for this session.
+ *
+ * Called internally when creating or resuming a session with an
+ * auto-mode-switch handler.
+ *
+ * @param handler
+ * the handler to invoke when an auto-mode-switch request is received
+ */
+ void registerAutoModeSwitchHandler(AutoModeSwitchHandler handler) {
+ autoModeSwitchHandler.set(handler);
+ }
+
/**
* Sets the capabilities reported by the host for this session.
*
@@ -1356,6 +1392,60 @@ CompletableFuture handleUserInputRequest(UserInputRequest req
}
}
+ /**
+ * Handles an exit-plan-mode request from the Copilot CLI.
+ *
+ * Called internally when the server sends an {@code exitPlanMode.request}.
+ *
+ * @param request
+ * the exit-plan-mode request
+ * @return a future that resolves with the user's decision
+ */
+ CompletableFuture handleExitPlanModeRequest(ExitPlanModeRequest request) {
+ ExitPlanModeHandler handler = exitPlanModeHandler.get();
+ if (handler == null) {
+ return CompletableFuture.completedFuture(new ExitPlanModeResult().setApproved(true));
+ }
+
+ try {
+ var invocation = new ExitPlanModeInvocation().setSessionId(sessionId);
+ return handler.handle(request, invocation).exceptionally(ex -> {
+ LOG.log(Level.SEVERE, "Exit plan mode handler threw an exception", ex);
+ throw new RuntimeException("Exit plan mode handler error", ex);
+ });
+ } catch (Exception e) {
+ LOG.log(Level.SEVERE, "Failed to process exit plan mode request", e);
+ return CompletableFuture.failedFuture(e);
+ }
+ }
+
+ /**
+ * Handles an auto-mode-switch request from the Copilot CLI.
+ *
+ * Called internally when the server sends an {@code autoModeSwitch.request}.
+ *
+ * @param request
+ * the auto-mode-switch request
+ * @return a future that resolves with the user's decision
+ */
+ CompletableFuture handleAutoModeSwitchRequest(AutoModeSwitchRequest request) {
+ AutoModeSwitchHandler handler = autoModeSwitchHandler.get();
+ if (handler == null) {
+ return CompletableFuture.completedFuture(AutoModeSwitchResponse.NO);
+ }
+
+ try {
+ var invocation = new AutoModeSwitchInvocation().setSessionId(sessionId);
+ return handler.handle(request, invocation).exceptionally(ex -> {
+ LOG.log(Level.SEVERE, "Auto mode switch handler threw an exception", ex);
+ throw new RuntimeException("Auto mode switch handler error", ex);
+ });
+ } catch (Exception e) {
+ LOG.log(Level.SEVERE, "Failed to process auto mode switch request", e);
+ return CompletableFuture.failedFuture(e);
+ }
+ }
+
/**
* Registers hook handlers for this session.
*
+ * Register an auto-mode-switch handler via
+ * {@link SessionConfig#setOnAutoModeSwitch(AutoModeSwitchHandler)} or
+ * {@link ResumeSessionConfig#setOnAutoModeSwitch(AutoModeSwitchHandler)}. When
+ * provided, the server routes {@code autoModeSwitch.request} callbacks to this
+ * handler.
+ *
+ *
+ *
+ * @see AutoModeSwitchRequest
+ * @see AutoModeSwitchResponse
+ * @since 1.0.8
+ */
+@FunctionalInterface
+public interface AutoModeSwitchHandler {
+
+ /**
+ * Handles an auto-mode-switch request from the agent.
+ *
+ * @param request
+ * the auto-mode-switch request containing the error code and
+ * retry-after seconds
+ * @param invocation
+ * context information about the invocation
+ * @return a future that resolves with the user's decision
+ */
+ CompletableFuture handle(AutoModeSwitchRequest request,
+ AutoModeSwitchInvocation invocation);
+}
diff --git a/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchInvocation.java b/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchInvocation.java
new file mode 100644
index 0000000000..278d10ccfd
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchInvocation.java
@@ -0,0 +1,36 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk.json;
+
+/**
+ * Context for an auto-mode-switch request invocation.
+ *
+ * @since 1.0.8
+ */
+public class AutoModeSwitchInvocation {
+
+ private String sessionId;
+
+ /**
+ * Gets the session ID.
+ *
+ * @return the session ID
+ */
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ /**
+ * Sets the session ID.
+ *
+ * @param sessionId
+ * the session ID
+ * @return this instance for method chaining
+ */
+ public AutoModeSwitchInvocation setSessionId(String sessionId) {
+ this.sessionId = sessionId;
+ return this;
+ }
+}
diff --git a/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchRequest.java b/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchRequest.java
new file mode 100644
index 0000000000..0ef784c02a
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchRequest.java
@@ -0,0 +1,68 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk.json;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Request to switch to auto mode after an eligible rate limit.
+ *
+ * This is sent by the server when the agent encounters a rate limit and wants
+ * to switch to an alternative model automatically.
+ *
+ * @since 1.0.8
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class AutoModeSwitchRequest {
+
+ @JsonProperty("errorCode")
+ private String errorCode;
+
+ @JsonProperty("retryAfterSeconds")
+ private Double retryAfterSeconds;
+
+ /**
+ * Gets the rate-limit error code that triggered the request.
+ *
+ * @return the error code, or {@code null}
+ */
+ public String getErrorCode() {
+ return errorCode;
+ }
+
+ /**
+ * Sets the rate-limit error code.
+ *
+ * @param errorCode
+ * the error code
+ * @return this instance for method chaining
+ */
+ public AutoModeSwitchRequest setErrorCode(String errorCode) {
+ this.errorCode = errorCode;
+ return this;
+ }
+
+ /**
+ * Gets the seconds until the rate limit resets, when known.
+ *
+ * @return the retry-after seconds, or {@code null}
+ */
+ public Double getRetryAfterSeconds() {
+ return retryAfterSeconds;
+ }
+
+ /**
+ * Sets the seconds until the rate limit resets.
+ *
+ * @param retryAfterSeconds
+ * the retry-after seconds
+ * @return this instance for method chaining
+ */
+ public AutoModeSwitchRequest setRetryAfterSeconds(Double retryAfterSeconds) {
+ this.retryAfterSeconds = retryAfterSeconds;
+ return this;
+ }
+}
diff --git a/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchResponse.java b/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchResponse.java
new file mode 100644
index 0000000000..c072b73e2b
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/json/AutoModeSwitchResponse.java
@@ -0,0 +1,40 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk.json;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * Response to an auto-mode-switch request.
+ *
+ * @since 1.0.8
+ */
+public enum AutoModeSwitchResponse {
+
+ /** Approve the switch for this rate-limit cycle. */
+ YES("yes"),
+
+ /** Approve and remember the choice for this session. */
+ YES_ALWAYS("yes_always"),
+
+ /** Decline the switch. */
+ NO("no");
+
+ private final String value;
+
+ AutoModeSwitchResponse(String value) {
+ this.value = value;
+ }
+
+ /**
+ * Gets the wire value of this response.
+ *
+ * @return the string value
+ */
+ @JsonValue
+ public String getValue() {
+ return value;
+ }
+}
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 3a0b90f19a..12bab4154c 100644
--- a/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/CreateSessionRequest.java
@@ -112,6 +112,12 @@ public final class CreateSessionRequest {
@JsonProperty("requestElicitation")
private Boolean requestElicitation;
+ @JsonProperty("requestExitPlanMode")
+ private Boolean requestExitPlanMode;
+
+ @JsonProperty("requestAutoModeSwitch")
+ private Boolean requestAutoModeSwitch;
+
@JsonProperty("modelCapabilities")
private ModelCapabilitiesOverride modelCapabilities;
@@ -419,6 +425,28 @@ public void setRequestElicitation(Boolean requestElicitation) {
this.requestElicitation = requestElicitation;
}
+ /** Gets the requestExitPlanMode flag. @return the flag */
+ public Boolean getRequestExitPlanMode() {
+ return requestExitPlanMode;
+ }
+
+ /** Sets the requestExitPlanMode flag. @param requestExitPlanMode the flag */
+ public void setRequestExitPlanMode(Boolean requestExitPlanMode) {
+ this.requestExitPlanMode = requestExitPlanMode;
+ }
+
+ /** Gets the requestAutoModeSwitch flag. @return the flag */
+ public Boolean getRequestAutoModeSwitch() {
+ return requestAutoModeSwitch;
+ }
+
+ /**
+ * Sets the requestAutoModeSwitch flag. @param requestAutoModeSwitch the flag
+ */
+ public void setRequestAutoModeSwitch(Boolean requestAutoModeSwitch) {
+ this.requestAutoModeSwitch = requestAutoModeSwitch;
+ }
+
/** Gets the model capabilities override. @return the override */
public ModelCapabilitiesOverride getModelCapabilities() {
return modelCapabilities;
diff --git a/src/main/java/com/github/copilot/sdk/json/ExitPlanModeHandler.java b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeHandler.java
new file mode 100644
index 0000000000..13ecbe075c
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeHandler.java
@@ -0,0 +1,48 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk.json;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Handler for exit-plan-mode requests from the agent.
+ *
+ * Register an exit-plan-mode handler via
+ * {@link SessionConfig#setOnExitPlanMode(ExitPlanModeHandler)} or
+ * {@link ResumeSessionConfig#setOnExitPlanMode(ExitPlanModeHandler)}. When
+ * provided, the server routes {@code exitPlanMode.request} callbacks to this
+ * handler.
+ *
+ *
Example Usage
+ *
+ *
{@code
+ * ExitPlanModeHandler handler = (request, invocation) -> {
+ * // Review the plan and decide whether to approve
+ * return CompletableFuture
+ * .completedFuture(new ExitPlanModeResult().setApproved(true).setSelectedAction("interactive"));
+ * };
+ *
+ * var session = client.createSession(new SessionConfig().setOnExitPlanMode(handler)).get();
+ * }
+ *
+ * @see ExitPlanModeRequest
+ * @see ExitPlanModeResult
+ * @since 1.0.8
+ */
+@FunctionalInterface
+public interface ExitPlanModeHandler {
+
+ /**
+ * Handles an exit-plan-mode request from the agent.
+ *
+ * @param request
+ * the exit-plan-mode request containing the summary, plan content,
+ * and available actions
+ * @param invocation
+ * context information about the invocation
+ * @return a future that resolves with the user's decision
+ */
+ CompletableFuture handle(ExitPlanModeRequest request, ExitPlanModeInvocation invocation);
+}
diff --git a/src/main/java/com/github/copilot/sdk/json/ExitPlanModeInvocation.java b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeInvocation.java
new file mode 100644
index 0000000000..6fd0231266
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeInvocation.java
@@ -0,0 +1,36 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk.json;
+
+/**
+ * Context for an exit-plan-mode request invocation.
+ *
+ * @since 1.0.8
+ */
+public class ExitPlanModeInvocation {
+
+ private String sessionId;
+
+ /**
+ * Gets the session ID.
+ *
+ * @return the session ID
+ */
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ /**
+ * Sets the session ID.
+ *
+ * @param sessionId
+ * the session ID
+ * @return this instance for method chaining
+ */
+ public ExitPlanModeInvocation setSessionId(String sessionId) {
+ this.sessionId = sessionId;
+ return this;
+ }
+}
diff --git a/src/main/java/com/github/copilot/sdk/json/ExitPlanModeRequest.java b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeRequest.java
new file mode 100644
index 0000000000..be09350ef3
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeRequest.java
@@ -0,0 +1,119 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk.json;
+
+import java.util.Collections;
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Request to exit plan mode and continue with a selected action.
+ *
+ * This is sent by the server when the agent wants to exit plan mode and
+ * requests user confirmation.
+ *
+ * @since 1.0.8
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class ExitPlanModeRequest {
+
+ @JsonProperty("summary")
+ private String summary = "";
+
+ @JsonProperty("planContent")
+ private String planContent;
+
+ @JsonProperty("actions")
+ private List actions;
+
+ @JsonProperty("recommendedAction")
+ private String recommendedAction = "autopilot";
+
+ /**
+ * Gets the summary of the plan or proposed next step.
+ *
+ * @return the summary
+ */
+ public String getSummary() {
+ return summary;
+ }
+
+ /**
+ * Sets the summary of the plan or proposed next step.
+ *
+ * @param summary
+ * the summary
+ * @return this instance for method chaining
+ */
+ public ExitPlanModeRequest setSummary(String summary) {
+ this.summary = summary;
+ return this;
+ }
+
+ /**
+ * Gets the full plan content, when available.
+ *
+ * @return the plan content, or {@code null} if not available
+ */
+ public String getPlanContent() {
+ return planContent;
+ }
+
+ /**
+ * Sets the full plan content.
+ *
+ * @param planContent
+ * the plan content
+ * @return this instance for method chaining
+ */
+ public ExitPlanModeRequest setPlanContent(String planContent) {
+ this.planContent = planContent;
+ return this;
+ }
+
+ /**
+ * Gets the available actions the user can select.
+ *
+ * @return the list of actions, or {@code null} if not specified
+ */
+ public List getActions() {
+ return actions == null ? null : Collections.unmodifiableList(actions);
+ }
+
+ /**
+ * Sets the available actions the user can select.
+ *
+ * @param actions
+ * the list of actions
+ * @return this instance for method chaining
+ */
+ public ExitPlanModeRequest setActions(List actions) {
+ this.actions = actions;
+ return this;
+ }
+
+ /**
+ * Gets the action recommended by the runtime.
+ *
+ * @return the recommended action
+ */
+ public String getRecommendedAction() {
+ return recommendedAction;
+ }
+
+ /**
+ * Sets the action recommended by the runtime.
+ *
+ * @param recommendedAction
+ * the recommended action
+ * @return this instance for method chaining
+ */
+ public ExitPlanModeRequest setRecommendedAction(String recommendedAction) {
+ this.recommendedAction = recommendedAction;
+ return this;
+ }
+}
diff --git a/src/main/java/com/github/copilot/sdk/json/ExitPlanModeResult.java b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeResult.java
new file mode 100644
index 0000000000..876e750b46
--- /dev/null
+++ b/src/main/java/com/github/copilot/sdk/json/ExitPlanModeResult.java
@@ -0,0 +1,87 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk.json;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Response to an exit-plan-mode request.
+ *
+ * @since 1.0.8
+ */
+public class ExitPlanModeResult {
+
+ @JsonProperty("approved")
+ private boolean approved = true;
+
+ @JsonProperty("selectedAction")
+ private String selectedAction;
+
+ @JsonProperty("feedback")
+ private String feedback;
+
+ /**
+ * Returns whether the user approved exiting plan mode.
+ *
+ * @return {@code true} if approved
+ */
+ public boolean isApproved() {
+ return approved;
+ }
+
+ /**
+ * Sets whether the user approved exiting plan mode.
+ *
+ * @param approved
+ * {@code true} if approved
+ * @return this instance for method chaining
+ */
+ public ExitPlanModeResult setApproved(boolean approved) {
+ this.approved = approved;
+ return this;
+ }
+
+ /**
+ * Gets the selected action, if the user chose one.
+ *
+ * @return the selected action, or {@code null}
+ */
+ public String getSelectedAction() {
+ return selectedAction;
+ }
+
+ /**
+ * Sets the selected action.
+ *
+ * @param selectedAction
+ * the selected action
+ * @return this instance for method chaining
+ */
+ public ExitPlanModeResult setSelectedAction(String selectedAction) {
+ this.selectedAction = selectedAction;
+ return this;
+ }
+
+ /**
+ * Gets optional feedback provided by the user.
+ *
+ * @return the feedback, or {@code null}
+ */
+ public String getFeedback() {
+ return feedback;
+ }
+
+ /**
+ * Sets feedback from the user.
+ *
+ * @param feedback
+ * the feedback text
+ * @return this instance for method chaining
+ */
+ public ExitPlanModeResult setFeedback(String feedback) {
+ this.feedback = feedback;
+ 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 cb52c62e02..f2caad7710 100644
--- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionConfig.java
@@ -66,6 +66,8 @@ public class ResumeSessionConfig {
private Consumer onEvent;
private List commands;
private ElicitationHandler onElicitationRequest;
+ private ExitPlanModeHandler onExitPlanMode;
+ private AutoModeSwitchHandler onAutoModeSwitch;
private String gitHubToken;
/**
@@ -766,6 +768,60 @@ public ResumeSessionConfig setOnElicitationRequest(ElicitationHandler onElicitat
return this;
}
+ /**
+ * Gets the exit-plan-mode request handler.
+ *
+ * @return the exit-plan-mode handler, or {@code null}
+ * @since 1.0.8
+ */
+ public ExitPlanModeHandler getOnExitPlanMode() {
+ return onExitPlanMode;
+ }
+
+ /**
+ * Sets a handler for exit-plan-mode requests from the server.
+ *
+ * When provided, the server will route {@code exitPlanMode.request} callbacks
+ * to this handler.
+ *
+ * @param onExitPlanMode
+ * the exit-plan-mode handler
+ * @return this config for method chaining
+ * @see ExitPlanModeHandler
+ * @since 1.0.8
+ */
+ public ResumeSessionConfig setOnExitPlanMode(ExitPlanModeHandler onExitPlanMode) {
+ this.onExitPlanMode = onExitPlanMode;
+ return this;
+ }
+
+ /**
+ * Gets the auto-mode-switch request handler.
+ *
+ * @return the auto-mode-switch handler, or {@code null}
+ * @since 1.0.8
+ */
+ public AutoModeSwitchHandler getOnAutoModeSwitch() {
+ return onAutoModeSwitch;
+ }
+
+ /**
+ * Sets a handler for auto-mode-switch requests from the server.
+ *
+ * When provided, the server will route {@code autoModeSwitch.request} callbacks
+ * to this handler.
+ *
+ * @param onAutoModeSwitch
+ * the auto-mode-switch handler
+ * @return this config for method chaining
+ * @see AutoModeSwitchHandler
+ * @since 1.0.8
+ */
+ public ResumeSessionConfig setOnAutoModeSwitch(AutoModeSwitchHandler onAutoModeSwitch) {
+ this.onAutoModeSwitch = onAutoModeSwitch;
+ return this;
+ }
+
/**
* Gets the GitHub token for per-session authentication.
*
@@ -839,6 +895,8 @@ public ResumeSessionConfig clone() {
copy.onEvent = this.onEvent;
copy.commands = this.commands != null ? new ArrayList<>(this.commands) : null;
copy.onElicitationRequest = this.onElicitationRequest;
+ copy.onExitPlanMode = this.onExitPlanMode;
+ copy.onAutoModeSwitch = this.onAutoModeSwitch;
copy.gitHubToken = this.gitHubToken;
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 9b2c17f1a4..a1af269700 100644
--- a/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
+++ b/src/main/java/com/github/copilot/sdk/json/ResumeSessionRequest.java
@@ -116,6 +116,12 @@ public final class ResumeSessionRequest {
@JsonProperty("requestElicitation")
private Boolean requestElicitation;
+ @JsonProperty("requestExitPlanMode")
+ private Boolean requestExitPlanMode;
+
+ @JsonProperty("requestAutoModeSwitch")
+ private Boolean requestAutoModeSwitch;
+
@JsonProperty("modelCapabilities")
private ModelCapabilitiesOverride modelCapabilities;
@@ -439,6 +445,28 @@ public void setRequestElicitation(Boolean requestElicitation) {
this.requestElicitation = requestElicitation;
}
+ /** Gets the requestExitPlanMode flag. @return the flag */
+ public Boolean getRequestExitPlanMode() {
+ return requestExitPlanMode;
+ }
+
+ /** Sets the requestExitPlanMode flag. @param requestExitPlanMode the flag */
+ public void setRequestExitPlanMode(Boolean requestExitPlanMode) {
+ this.requestExitPlanMode = requestExitPlanMode;
+ }
+
+ /** Gets the requestAutoModeSwitch flag. @return the flag */
+ public Boolean getRequestAutoModeSwitch() {
+ return requestAutoModeSwitch;
+ }
+
+ /**
+ * Sets the requestAutoModeSwitch flag. @param requestAutoModeSwitch the flag
+ */
+ public void setRequestAutoModeSwitch(Boolean requestAutoModeSwitch) {
+ this.requestAutoModeSwitch = requestAutoModeSwitch;
+ }
+
/** Gets the model capabilities override. @return the override */
public ModelCapabilitiesOverride getModelCapabilities() {
return modelCapabilities;
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 a4b2769b74..51cad884ed 100644
--- a/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
+++ b/src/main/java/com/github/copilot/sdk/json/SessionConfig.java
@@ -66,6 +66,8 @@ public class SessionConfig {
private Consumer onEvent;
private List commands;
private ElicitationHandler onElicitationRequest;
+ private ExitPlanModeHandler onExitPlanMode;
+ private AutoModeSwitchHandler onAutoModeSwitch;
private String gitHubToken;
/**
@@ -818,6 +820,60 @@ public SessionConfig setOnElicitationRequest(ElicitationHandler onElicitationReq
return this;
}
+ /**
+ * Gets the exit-plan-mode request handler.
+ *
+ * @return the exit-plan-mode handler, or {@code null}
+ * @since 1.0.8
+ */
+ public ExitPlanModeHandler getOnExitPlanMode() {
+ return onExitPlanMode;
+ }
+
+ /**
+ * Sets a handler for exit-plan-mode requests from the server.
+ *
+ * When provided, the server will route {@code exitPlanMode.request} callbacks
+ * to this handler.
+ *
+ * @param onExitPlanMode
+ * the exit-plan-mode handler
+ * @return this config instance for method chaining
+ * @see ExitPlanModeHandler
+ * @since 1.0.8
+ */
+ public SessionConfig setOnExitPlanMode(ExitPlanModeHandler onExitPlanMode) {
+ this.onExitPlanMode = onExitPlanMode;
+ return this;
+ }
+
+ /**
+ * Gets the auto-mode-switch request handler.
+ *
+ * @return the auto-mode-switch handler, or {@code null}
+ * @since 1.0.8
+ */
+ public AutoModeSwitchHandler getOnAutoModeSwitch() {
+ return onAutoModeSwitch;
+ }
+
+ /**
+ * Sets a handler for auto-mode-switch requests from the server.
+ *
+ * When provided, the server will route {@code autoModeSwitch.request} callbacks
+ * to this handler.
+ *
+ * @param onAutoModeSwitch
+ * the auto-mode-switch handler
+ * @return this config instance for method chaining
+ * @see AutoModeSwitchHandler
+ * @since 1.0.8
+ */
+ public SessionConfig setOnAutoModeSwitch(AutoModeSwitchHandler onAutoModeSwitch) {
+ this.onAutoModeSwitch = onAutoModeSwitch;
+ return this;
+ }
+
/**
* Gets the GitHub token for per-session authentication.
*
@@ -891,6 +947,8 @@ public SessionConfig clone() {
copy.onEvent = this.onEvent;
copy.commands = this.commands != null ? new ArrayList<>(this.commands) : null;
copy.onElicitationRequest = this.onElicitationRequest;
+ copy.onExitPlanMode = this.onExitPlanMode;
+ copy.onAutoModeSwitch = this.onAutoModeSwitch;
copy.gitHubToken = this.gitHubToken;
return copy;
}
From f55af2d2be1d87c31d46eb64a5cb96f393317df6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 11 May 2026 18:12:21 +0000
Subject: [PATCH 08/18] Update .lastmerge to
4a0437bb03a0b60a1867f14ae8e3faf053afa5aa, sync pom.xml CLI version, and
update scripts/codegen @github/copilot version
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
---
.lastmerge | 2 +-
pom.xml | 2 +-
scripts/codegen/package-lock.json | 56 +++++++++++++++----------------
scripts/codegen/package.json | 2 +-
4 files changed, 31 insertions(+), 31 deletions(-)
diff --git a/.lastmerge b/.lastmerge
index 142f4f7ab1..b28867a960 100644
--- a/.lastmerge
+++ b/.lastmerge
@@ -1 +1 @@
-066a69c1e849adf1bd98564ab1b52316ec471182
+4a0437bb03a0b60a1867f14ae8e3faf053afa5aa
diff --git a/pom.xml b/pom.xml
index 832f2f2c9d..c8930b3d10 100644
--- a/pom.xml
+++ b/pom.xml
@@ -94,7 +94,7 @@
reference-impl-sync workflow and deal with the subsequent
PR.
-->
- ^1.0.44-2
+ ^1.0.44-3
diff --git a/scripts/codegen/package-lock.json b/scripts/codegen/package-lock.json
index f414fec716..0846fc42e6 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-2",
+ "@github/copilot": "^1.0.44-3",
"json-schema": "^0.4.0",
"tsx": "^4.20.6"
}
@@ -428,26 +428,26 @@
}
},
"node_modules/@github/copilot": {
- "version": "1.0.44-2",
- "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.44-2.tgz",
- "integrity": "sha512-MUIR4w+oXjbg1jwUS8B86eMd/bV2gVKZ61a/aEUE4gUrFFpGXO0tNk9OkfLSH5cmlhJY6lzMzb+kKQWoeAbbNQ==",
+ "version": "1.0.45",
+ "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.45.tgz",
+ "integrity": "sha512-2QADgQcw/d0GFqTq2+nHwX152ZRvZxW0CHONG5d1RCs6YJtdr/GdbnMYYeRH2BiBIhnfkcvF50ImCRvsS5Tnwg==",
"license": "SEE LICENSE IN LICENSE.md",
"bin": {
"copilot": "npm-loader.js"
},
"optionalDependencies": {
- "@github/copilot-darwin-arm64": "1.0.44-2",
- "@github/copilot-darwin-x64": "1.0.44-2",
- "@github/copilot-linux-arm64": "1.0.44-2",
- "@github/copilot-linux-x64": "1.0.44-2",
- "@github/copilot-win32-arm64": "1.0.44-2",
- "@github/copilot-win32-x64": "1.0.44-2"
+ "@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"
}
},
"node_modules/@github/copilot-darwin-arm64": {
- "version": "1.0.44-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.44-2.tgz",
- "integrity": "sha512-6o/pvew0FZJG+8saG1K/L1pUIvpz4AWkZitiqH36tDfXdXKx/PUQ+zaFg/KPeHNnxtal5OdE/7iyrJwIqm2gPg==",
+ "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==",
"cpu": [
"arm64"
],
@@ -461,9 +461,9 @@
}
},
"node_modules/@github/copilot-darwin-x64": {
- "version": "1.0.44-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.44-2.tgz",
- "integrity": "sha512-OMNoLNFYUynB4wiplSh4gtD5zVlvfWMKc0jKQ0oItJLGO8GRL9X0ZB2ONB+7JpVvPidz0Yy4+jU0zWNXEjMM5g==",
+ "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==",
"cpu": [
"x64"
],
@@ -477,9 +477,9 @@
}
},
"node_modules/@github/copilot-linux-arm64": {
- "version": "1.0.44-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.44-2.tgz",
- "integrity": "sha512-5WGRADU08hqBTWmQ6JVOYMximzsXGuOdFF4GFRQqfsCR8k4RE8fdPWQJa92BpqMgGWwEVPemq0wB3D4hDM5eWw==",
+ "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==",
"cpu": [
"arm64"
],
@@ -493,9 +493,9 @@
}
},
"node_modules/@github/copilot-linux-x64": {
- "version": "1.0.44-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.44-2.tgz",
- "integrity": "sha512-4ZnA2QxEwgrdCePdS5OjuksEGFpJrXgofuELANCpDSHwR3eTV7PynVyqhG6Et7ktN2KzHk7zf8kvtiWVCOxvFg==",
+ "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==",
"cpu": [
"x64"
],
@@ -509,9 +509,9 @@
}
},
"node_modules/@github/copilot-win32-arm64": {
- "version": "1.0.44-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.44-2.tgz",
- "integrity": "sha512-klgSdBZblz9O8BRnTh9uk9uO/INQwVeTBagXuJO7MrZ7JCfBVJyFUYky2tKIjFxlwefyhrRZuniqYeOI9fQc+A==",
+ "version": "1.0.45",
+ "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.45.tgz",
+ "integrity": "sha512-lhcTlKs7MWMzIXv21hUSpL4aFW49jqVhNrQKaB8sYk2nzvGRJvNwTcBS1Tn5ndXlPzQ9P/p9B6B5uwwmZ1vHHw==",
"cpu": [
"arm64"
],
@@ -525,9 +525,9 @@
}
},
"node_modules/@github/copilot-win32-x64": {
- "version": "1.0.44-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.44-2.tgz",
- "integrity": "sha512-ziq3abdbMCqtAqdiEWWf6cn0whlWss7rC9VMsO/Vx2gjSEVCeJkmIiRiQO45WikheyXyxEmCTAvOwZLQvs+I9g==",
+ "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==",
"cpu": [
"x64"
],
diff --git a/scripts/codegen/package.json b/scripts/codegen/package.json
index 66d5565e86..6dde795791 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-2",
+ "@github/copilot": "^1.0.44-3",
"json-schema": "^0.4.0",
"tsx": "^4.20.6"
}
From 3e1614bbf83666b00972daf57ff5dad78e100f1d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 11 May 2026 18:12:34 +0000
Subject: [PATCH 09/18] Add unit/E2E tests and documentation for mode handler
APIs
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
---
src/site/markdown/advanced.md | 61 ++++++++
.../github/copilot/sdk/ConfigCloneTest.java | 30 ++++
.../github/copilot/sdk/ModeHandlersTest.java | 145 ++++++++++++++++++
.../sdk/SessionRequestBuilderTest.java | 124 +++++++++++++++
4 files changed, 360 insertions(+)
create mode 100644 src/test/java/com/github/copilot/sdk/ModeHandlersTest.java
diff --git a/src/site/markdown/advanced.md b/src/site/markdown/advanced.md
index ccf386640f..5c46313569 100644
--- a/src/site/markdown/advanced.md
+++ b/src/site/markdown/advanced.md
@@ -53,6 +53,9 @@ This guide covers advanced scenarios for extending and customizing your Copilot
- [Incoming Elicitation Handler](#Incoming_Elicitation_Handler)
- [Session Capabilities](#Session_Capabilities)
- [Outgoing Elicitation via session.getUi()](#Outgoing_Elicitation_via_session.getUi)
+- [Mode Handlers](#Mode_Handlers)
+ - [Exit Plan Mode](#Exit_Plan_Mode)
+ - [Auto Mode Switch](#Auto_Mode_Switch)
- [Getting Session Metadata by ID](#Getting_Session_Metadata_by_ID)
---
@@ -1267,6 +1270,64 @@ All `getUi()` methods throw `IllegalStateException` if the host does not support
---
+## Mode Handlers
+
+Mode handlers let your application respond to mode transitions requested by the Copilot CLI.
+
+### Exit Plan Mode
+
+When the model finishes creating a plan and wants to transition out of plan mode, it invokes the `exitPlanMode` handler. Register the handler via `SessionConfig.setOnExitPlanMode()`:
+
+```java
+var session = client.createSession(new SessionConfig()
+ .setOnExitPlanMode((request, invocation) -> {
+ System.out.println("Plan summary: " + request.getSummary());
+ System.out.println("Available actions: " + request.getActions());
+ System.out.println("Recommended: " + request.getRecommendedAction());
+
+ return CompletableFuture.completedFuture(
+ new ExitPlanModeResult()
+ .setApproved(true)
+ .setSelectedAction("interactive")
+ .setFeedback("Looks good, proceed!"));
+ })).get();
+```
+
+When no handler is registered, the SDK automatically approves the plan (`approved=true`). The handler receives an `ExitPlanModeRequest` with:
+
+| Field | Description |
+|---------------------|-------------------------------------------------|
+| `summary` | Summary of the plan that was created |
+| `planContent` | Full content of the plan file |
+| `actions` | Available actions (e.g., interactive, autopilot) |
+| `recommendedAction` | The recommended action for the user |
+
+### Auto Mode Switch
+
+When the model encounters a rate limit or similar constraint, it may request to switch modes automatically. Register the handler via `SessionConfig.setOnAutoModeSwitch()`:
+
+```java
+var session = client.createSession(new SessionConfig()
+ .setOnAutoModeSwitch((request, invocation) -> {
+ System.out.println("Error: " + request.getErrorCode());
+ System.out.println("Retry after: " + request.getRetryAfterSeconds() + "s");
+
+ return CompletableFuture.completedFuture(AutoModeSwitchResponse.YES);
+ })).get();
+```
+
+When no handler is registered, the SDK returns `NO` (declining the mode switch). The response options are:
+
+| Response | Description |
+|---------------------------------|------------------------------------------|
+| `AutoModeSwitchResponse.YES` | Allow the mode switch this time |
+| `AutoModeSwitchResponse.YES_ALWAYS`| Always allow automatic mode switches |
+| `AutoModeSwitchResponse.NO` | Decline the mode switch |
+
+Both handlers are also available on `ResumeSessionConfig` for resumed sessions.
+
+---
+
## Getting Session Metadata by ID
Retrieve metadata for a specific session without listing all sessions:
diff --git a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java
index f7ce3aa4de..7abc47e4a4 100644
--- a/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java
+++ b/src/test/java/com/github/copilot/sdk/ConfigCloneTest.java
@@ -16,8 +16,10 @@
import org.junit.jupiter.api.Test;
import com.github.copilot.sdk.generated.SessionEvent;
+import com.github.copilot.sdk.json.AutoModeSwitchResponse;
import com.github.copilot.sdk.json.CopilotClientOptions;
import com.github.copilot.sdk.json.DefaultAgentConfig;
+import com.github.copilot.sdk.json.ExitPlanModeResult;
import com.github.copilot.sdk.json.InfiniteSessionConfig;
import com.github.copilot.sdk.json.MessageOptions;
import com.github.copilot.sdk.json.ModelInfo;
@@ -375,4 +377,32 @@ void copilotClientOptionsSessionIdleTimeoutCloned() {
assertEquals(600, cloned.getSessionIdleTimeoutSeconds());
}
+
+ @Test
+ void sessionConfigCloneCopiesModeSwitchHandlers() {
+ SessionConfig original = new SessionConfig();
+ original.setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+ original.setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ SessionConfig cloned = original.clone();
+
+ assertSame(original.getOnExitPlanMode(), cloned.getOnExitPlanMode());
+ assertSame(original.getOnAutoModeSwitch(), cloned.getOnAutoModeSwitch());
+ }
+
+ @Test
+ void resumeSessionConfigCloneCopiesModeSwitchHandlers() {
+ ResumeSessionConfig original = new ResumeSessionConfig();
+ original.setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+ original.setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ ResumeSessionConfig cloned = original.clone();
+
+ assertSame(original.getOnExitPlanMode(), cloned.getOnExitPlanMode());
+ assertSame(original.getOnAutoModeSwitch(), cloned.getOnAutoModeSwitch());
+ }
}
diff --git a/src/test/java/com/github/copilot/sdk/ModeHandlersTest.java b/src/test/java/com/github/copilot/sdk/ModeHandlersTest.java
new file mode 100644
index 0000000000..1c2da5d094
--- /dev/null
+++ b/src/test/java/com/github/copilot/sdk/ModeHandlersTest.java
@@ -0,0 +1,145 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+package com.github.copilot.sdk;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import com.github.copilot.sdk.generated.ExitPlanModeCompletedEvent;
+import com.github.copilot.sdk.generated.ExitPlanModeRequestedEvent;
+import com.github.copilot.sdk.json.AutoModeSwitchRequest;
+import com.github.copilot.sdk.json.AutoModeSwitchResponse;
+import com.github.copilot.sdk.json.CopilotClientOptions;
+import com.github.copilot.sdk.json.ExitPlanModeRequest;
+import com.github.copilot.sdk.json.ExitPlanModeResult;
+import com.github.copilot.sdk.json.MessageOptions;
+import com.github.copilot.sdk.json.PermissionHandler;
+import com.github.copilot.sdk.json.SessionConfig;
+
+/**
+ * E2E tests for exit-plan-mode and auto-mode-switch handler APIs.
+ *
+ *
+ * Ported from {@code ModeHandlersE2ETests.cs} in the reference implementation
+ * dotnet SDK.
+ *
+ */
+public class ModeHandlersTest {
+
+ private static final String TOKEN = "mode-handler-token";
+
+ private static E2ETestContext ctx;
+
+ @BeforeAll
+ static void setup() throws Exception {
+ ctx = E2ETestContext.create();
+ }
+
+ @AfterAll
+ static void teardown() throws Exception {
+ if (ctx != null) {
+ ctx.close();
+ }
+ }
+
+ private CopilotClient createAuthenticatedClient() {
+ Map env = new HashMap<>(ctx.getEnvironment());
+ env.put("COPILOT_DEBUG_GITHUB_API_URL", ctx.getProxyUrl());
+
+ return ctx.createClient(new CopilotClientOptions().setEnvironment(env));
+ }
+
+ private void configureAuthenticatedUser() throws Exception {
+ ctx.setCopilotUserByToken(TOKEN, "mode-handler-user", "individual_pro", ctx.getProxyUrl(),
+ "https://localhost:1/telemetry", "mode-handler-tracking-id");
+ }
+
+ @Test
+ void shouldInvokeExitPlanModeHandlerWhenModelUsesTool() throws Exception {
+ final String summary = "Greeting file implementation plan";
+ configureAuthenticatedUser();
+
+ var handlerCalled = new CompletableFuture();
+
+ try (var client = createAuthenticatedClient()) {
+ var session = client.createSession(new SessionConfig().setGitHubToken(TOKEN)
+ .setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setOnExitPlanMode((request, invocation) -> {
+ handlerCalled.complete(request);
+ return CompletableFuture.completedFuture(new ExitPlanModeResult().setApproved(true)
+ .setSelectedAction("interactive").setFeedback("Approved by the Java E2E test"));
+ })).get(30, TimeUnit.SECONDS);
+
+ var requestedEvent = new CompletableFuture();
+ var completedEvent = new CompletableFuture();
+
+ session.on(event -> {
+ if (event instanceof ExitPlanModeRequestedEvent requested
+ && summary.equals(requested.getData().summary())) {
+ requestedEvent.complete(requested);
+ } else if (event instanceof ExitPlanModeCompletedEvent completed
+ && Boolean.TRUE.equals(completed.getData().approved())
+ && "interactive".equals(completed.getData().selectedAction())) {
+ completedEvent.complete(completed);
+ }
+ });
+
+ var response = session.sendAndWait(new MessageOptions().setPrompt(
+ "Create a brief implementation plan for adding a greeting.txt file, then request approval with exit_plan_mode.")
+ .setMode("plan")).get(120, TimeUnit.SECONDS);
+
+ var request = handlerCalled.get(10, TimeUnit.SECONDS);
+ assertEquals(summary, request.getSummary());
+ assertNotNull(request.getActions());
+ assertTrue(request.getActions().contains("interactive"));
+ assertNotNull(request.getPlanContent());
+
+ var reqEvent = requestedEvent.get(10, TimeUnit.SECONDS);
+ assertEquals(request.getSummary(), reqEvent.getData().summary());
+
+ var compEvent = completedEvent.get(10, TimeUnit.SECONDS);
+ assertTrue(compEvent.getData().approved());
+ assertEquals("interactive", compEvent.getData().selectedAction());
+
+ assertNotNull(response);
+
+ session.close();
+ }
+ }
+
+ @Test
+ void shouldInvokeAutoModeSwitchHandlerWhenRateLimited() throws Exception {
+ configureAuthenticatedUser();
+
+ var handlerCalled = new CompletableFuture();
+
+ try (var client = createAuthenticatedClient()) {
+ var session = client.createSession(
+ new SessionConfig().setGitHubToken(TOKEN).setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
+ .setOnAutoModeSwitch((request, invocation) -> {
+ handlerCalled.complete(request);
+ return CompletableFuture.completedFuture(AutoModeSwitchResponse.YES);
+ }))
+ .get(30, TimeUnit.SECONDS);
+
+ session.sendAndWait(new MessageOptions()
+ .setPrompt("Explain that auto mode recovered from a rate limit in one short sentence."))
+ .get(30, TimeUnit.SECONDS);
+
+ var request = handlerCalled.get(30, TimeUnit.SECONDS);
+ assertEquals("user_weekly_rate_limited", request.getErrorCode());
+ assertEquals(1.0, request.getRetryAfterSeconds());
+
+ session.close();
+ }
+ }
+}
diff --git a/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java b/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java
index 0d13576eb8..43703831a8 100644
--- a/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java
+++ b/src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java
@@ -12,11 +12,13 @@
import org.junit.jupiter.api.Test;
+import com.github.copilot.sdk.json.AutoModeSwitchResponse;
import com.github.copilot.sdk.json.CreateSessionRequest;
import com.github.copilot.sdk.json.DefaultAgentConfig;
import com.github.copilot.sdk.json.ElicitationHandler;
import com.github.copilot.sdk.json.ElicitationResult;
import com.github.copilot.sdk.json.ElicitationResultAction;
+import com.github.copilot.sdk.json.ExitPlanModeResult;
import com.github.copilot.sdk.json.ResumeSessionConfig;
import com.github.copilot.sdk.json.ResumeSessionRequest;
import com.github.copilot.sdk.json.SessionConfig;
@@ -535,4 +537,126 @@ void testResumeRequestOmitsEnableSessionTelemetryWhenNull() throws Exception {
var json = mapper.writeValueAsString(request);
assertFalse(json.contains("enableSessionTelemetry"), "enableSessionTelemetry should be omitted when null");
}
+
+ // =========================================================================
+ // Mode handler request flags
+ // =========================================================================
+
+ @Test
+ void testBuildCreateRequestWithExitPlanModeHandler() {
+ var config = new SessionConfig().setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+
+ assertTrue(request.getRequestExitPlanMode());
+ }
+
+ @Test
+ void testBuildCreateRequestWithAutoModeSwitchHandler() {
+ var config = new SessionConfig().setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+
+ assertTrue(request.getRequestAutoModeSwitch());
+ }
+
+ @Test
+ void testBuildCreateRequestWithoutModeHandlers() {
+ var config = new SessionConfig();
+
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+
+ assertNull(request.getRequestExitPlanMode());
+ assertNull(request.getRequestAutoModeSwitch());
+ }
+
+ @Test
+ void testBuildResumeRequestWithExitPlanModeHandler() {
+ var config = new ResumeSessionConfig().setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("session-1", config);
+
+ assertTrue(request.getRequestExitPlanMode());
+ }
+
+ @Test
+ void testBuildResumeRequestWithAutoModeSwitchHandler() {
+ var config = new ResumeSessionConfig().setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("session-1", config);
+
+ assertTrue(request.getRequestAutoModeSwitch());
+ }
+
+ @Test
+ void configureSessionWithExitPlanModeHandler_registersHandler() {
+ CopilotSession session = new CopilotSession("session-1", null);
+
+ var config = new SessionConfig().setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+
+ SessionRequestBuilder.configureSession(session, config);
+ }
+
+ @Test
+ void configureSessionWithAutoModeSwitchHandler_registersHandler() {
+ CopilotSession session = new CopilotSession("session-1", null);
+
+ var config = new SessionConfig().setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ SessionRequestBuilder.configureSession(session, config);
+ }
+
+ @Test
+ void configureResumedSessionWithExitPlanModeHandler_registersHandler() {
+ CopilotSession session = new CopilotSession("session-1", null);
+
+ var config = new ResumeSessionConfig().setOnExitPlanMode(
+ (request, invocation) -> CompletableFuture.completedFuture(new ExitPlanModeResult()));
+
+ SessionRequestBuilder.configureSession(session, config);
+ }
+
+ @Test
+ void configureResumedSessionWithAutoModeSwitchHandler_registersHandler() {
+ CopilotSession session = new CopilotSession("session-1", null);
+
+ var config = new ResumeSessionConfig().setOnAutoModeSwitch(
+ (request, invocation) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ SessionRequestBuilder.configureSession(session, config);
+ }
+
+ @Test
+ void testCreateRequestSerializesModeFlags() throws Exception {
+ var config = new SessionConfig()
+ .setOnExitPlanMode((r, i) -> CompletableFuture.completedFuture(new ExitPlanModeResult()))
+ .setOnAutoModeSwitch((r, i) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ CreateSessionRequest request = SessionRequestBuilder.buildCreateRequest(config);
+ var mapper = JsonRpcClient.getObjectMapper();
+ var json = mapper.writeValueAsString(request);
+
+ assertTrue(json.contains("\"requestExitPlanMode\":true"));
+ assertTrue(json.contains("\"requestAutoModeSwitch\":true"));
+ }
+
+ @Test
+ void testResumeRequestSerializesModeFlags() throws Exception {
+ var config = new ResumeSessionConfig()
+ .setOnExitPlanMode((r, i) -> CompletableFuture.completedFuture(new ExitPlanModeResult()))
+ .setOnAutoModeSwitch((r, i) -> CompletableFuture.completedFuture(AutoModeSwitchResponse.NO));
+
+ ResumeSessionRequest request = SessionRequestBuilder.buildResumeRequest("session-1", config);
+ var mapper = JsonRpcClient.getObjectMapper();
+ var json = mapper.writeValueAsString(request);
+
+ assertTrue(json.contains("\"requestExitPlanMode\":true"));
+ assertTrue(json.contains("\"requestAutoModeSwitch\":true"));
+ }
}
From a427c7f6073a07d03acd852a158b3dc9afa8ac43 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Mon, 11 May 2026 18:44:37 +0000
Subject: [PATCH 10/18] Regenerate codegen output
Auto-committed by codegen-check workflow.
---
.../sdk/generated/AssistantMessageEvent.java | 6 ++++
.../sdk/generated/SessionStartEvent.java | 4 ++-
.../sdk/generated/rpc/SessionCommandsApi.java | 13 ++++++++
...nCommandsRespondToQueuedCommandParams.java | 31 +++++++++++++++++
...nCommandsRespondToQueuedCommandResult.java | 27 +++++++++++++++
.../sdk/generated/rpc/SessionTasksApi.java | 15 +++++++++
.../rpc/SessionTasksSendMessageParams.java | 33 +++++++++++++++++++
.../rpc/SessionTasksSendMessageResult.java | 29 ++++++++++++++++
.../SessionWorkspacesGetWorkspaceResult.java | 23 -------------
9 files changed, 157 insertions(+), 24 deletions(-)
create mode 100644 src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsRespondToQueuedCommandParams.java
create mode 100644 src/generated/java/com/github/copilot/sdk/generated/rpc/SessionCommandsRespondToQueuedCommandResult.java
create mode 100644 src/generated/java/com/github/copilot/sdk/generated/rpc/SessionTasksSendMessageParams.java
create mode 100644 src/generated/java/com/github/copilot/sdk/generated/rpc/SessionTasksSendMessageResult.java
diff --git a/src/generated/java/com/github/copilot/sdk/generated/AssistantMessageEvent.java b/src/generated/java/com/github/copilot/sdk/generated/AssistantMessageEvent.java
index 2ff43e67a8..3ac0b9780a 100644
--- a/src/generated/java/com/github/copilot/sdk/generated/AssistantMessageEvent.java
+++ b/src/generated/java/com/github/copilot/sdk/generated/AssistantMessageEvent.java
@@ -38,6 +38,8 @@ public final class AssistantMessageEvent extends SessionEvent {
public record AssistantMessageEventData(
/** Unique identifier for this assistant message */
@JsonProperty("messageId") String messageId,
+ /** Model that produced this assistant message, if known */
+ @JsonProperty("model") String model,
/** The assistant's text response content */
@JsonProperty("content") String content,
/** Tool invocations requested by the assistant in this message */
@@ -56,6 +58,10 @@ public record AssistantMessageEventData(
@JsonProperty("interactionId") String interactionId,
/** GitHub request tracing ID (x-github-request-id header) for correlating with server-side logs */
@JsonProperty("requestId") String requestId,
+ /** Raw Anthropic content array with advisor blocks (server_tool_use, advisor_tool_result) for verbatim round-tripping */
+ @JsonProperty("anthropicAdvisorBlocks") List