diff --git a/.claude/skills/agentic-merge-reference-impl/SKILL.md b/.claude/skills/agentic-merge-reference-impl/SKILL.md
new file mode 100644
index 000000000..421ea147b
--- /dev/null
+++ b/.claude/skills/agentic-merge-reference-impl/SKILL.md
@@ -0,0 +1,7 @@
+---
+name: agentic-merge-reference-impl
+description: Merge reference implementation changes from the official Copilot SDK into this Java SDK.
+license: MIT
+---
+
+Follow instructions in the [agentic-merge-reference-impl prompt](../../../.github/prompts/agentic-merge-reference-impl.prompt.md) to merge reference implementation changes from the official Copilot SDK into this Java SDK.
diff --git a/.claude/skills/agentic-merge-upstream/SKILL.md b/.claude/skills/agentic-merge-upstream/SKILL.md
deleted file mode 100644
index 32428db89..000000000
--- a/.claude/skills/agentic-merge-upstream/SKILL.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-name: agentic-merge-upstream
-description: Merge upstream changes from the official Copilot SDK into this Java SDK.
-license: MIT
----
-
-Follow instructions in the [agentic-merge-upstream prompt](../../../.github/prompts/agentic-merge-upstream.prompt.md) to merge upstream changes from the official Copilot SDK into this Java SDK.
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
index c1965c216..adb3de78e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,2 @@
-.github/workflows/*.lock.yml linguist-generated=true merge=ours
\ No newline at end of file
+.github/workflows/*.lock.yml linguist-generated=true merge=ours
+src/generated/java/** eol=lf linguist-generated=true
diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json
index 0da84b9c6..1b8b4b56b 100644
--- a/.github/aw/actions-lock.json
+++ b/.github/aw/actions-lock.json
@@ -5,6 +5,11 @@
"version": "v8",
"sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd"
},
+ "actions/github-script@v9": {
+ "repo": "actions/github-script",
+ "version": "v9",
+ "sha": "373c709c69115d41ff229c7e5df9f8788daa9553"
+ },
"actions/setup-java@v4": {
"repo": "actions/setup-java",
"version": "v4",
@@ -15,10 +20,10 @@
"version": "v4",
"sha": "49933ea5288caeca8642d1e84afbd3f7d6820020"
},
- "github/gh-aw-actions/setup@v0.63.1": {
+ "github/gh-aw-actions/setup@v0.68.3": {
"repo": "github/gh-aw-actions/setup",
- "version": "v0.63.1",
- "sha": "53e09ec0be6271e81a69f51ef93f37212c8834b0"
+ "version": "v0.68.3",
+ "sha": "ba90f2186d7ad780ec640f364005fa24e797b360"
},
"github/gh-aw/actions/setup@v0.51.6": {
"repo": "github/gh-aw/actions/setup",
diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg
index 482adb13b..65756f841 100644
--- a/.github/badges/jacoco.svg
+++ b/.github/badges/jacoco.svg
@@ -12,7 +12,7 @@
coveragecoverage
- 84.7%
- 84.7%
+ 87.2%
+ 87.2%
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index d7dafb081..d02978421 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -9,7 +9,7 @@ These instructions guide GitHub Copilot when assisting with this repository. The
- **Tech Stack**: Java 17+, Maven, Jackson for JSON, JUnit for testing
- **Purpose**: Provide a Java SDK for programmatic control of GitHub Copilot CLI
- **Architecture**: JSON-RPC client communicating with Copilot CLI over stdio
-- **Key Goals**: Maintain parity with upstream .NET SDK while following Java idioms
+- **Key Goals**: Maintain parity with reference implementation .NET SDK while following Java idioms
## Build & Test Commands
@@ -77,7 +77,7 @@ mvn test -Dtest=CopilotClientTest
- `com.github.copilot.sdk` - Core classes (CopilotClient, CopilotSession, JsonRpcClient)
- `com.github.copilot.sdk.json` - DTOs, request/response types, handler interfaces (SessionConfig, MessageOptions, ToolDefinition, etc.)
-- `com.github.copilot.sdk.events` - Event types for session streaming (AssistantMessageEvent, SessionIdleEvent, ToolExecutionStartEvent, etc.)
+- `com.github.copilot.sdk.generated` - Generated event types for session streaming (SessionEvent, AssistantMessageEvent, SessionIdleEvent, ToolExecutionStartEvent, etc.)
### Test Infrastructure
@@ -85,13 +85,13 @@ Tests use the official copilot-sdk test harness from `https://github.com/github/
- **E2ETestContext** - Manages test environment with CapiProxy for deterministic API responses
- **CapiProxy** - Node.js-based replaying proxy using YAML snapshots from `test/snapshots/`
-- Test snapshots are stored in the upstream repo's `test/snapshots/` directory
+- Test snapshots are stored in the reference implementation repo's `test/snapshots/` directory
## Key Conventions
-### Upstream Merging
+### Reference Implementation Merging
-This SDK tracks the official .NET implementation at `github/copilot-sdk`. The `.lastmerge` file contains the last merged upstream commit hash. Use the `agentic-merge-upstream` skill (see `.github/prompts/agentic-merge-upstream.prompt.md`) to port changes.
+This SDK tracks the official .NET implementation at `github/copilot-sdk`. The `.lastmerge` file contains the last merged reference implementation commit hash. Use the `agentic-merge-reference-impl` skill (see `.github/prompts/agentic-merge-reference-impl.prompt.md`) to port changes.
When porting from .NET:
- Adapt to Java idioms, don't copy C# patterns directly
@@ -104,7 +104,7 @@ When porting from .NET:
- 4-space indentation (enforced by Spotless with Eclipse formatter)
- Fluent setter pattern for configuration classes (e.g., `new SessionConfig().setModel("gpt-5").setTools(tools)`)
- Public APIs require Javadoc (enforced by Checkstyle, except `json` and `events` packages)
-- Pre-commit hook runs `mvn spotless:check` - enable with: `git config core.hooksPath .githooks`
+- Pre-commit hook runs `mvn spotless:check` - Must be manually enabled with: `git config core.hooksPath .githooks`, except in the Copilot coding agent environment. This hook is explicitly enabled in the Copilot coding agent environment. See [copilot-setup-steps.yml](workflows/copilot-setup-steps.yml).
### Handler Pattern
@@ -213,11 +213,13 @@ Test method names are converted to lowercase snake_case for snapshot filenames t
### What NOT to Modify
+- **DO NOT** edit, create, or delete any file under `src/generated/java/` — these are **auto-generated** by `scripts/codegen/java.ts` and will be overwritten. To update generated code, run `mvn generate-sources -Pcodegen` or trigger the `update-copilot-dependency.yml` workflow.
+ If you need to get a new version of the schemas, run `mvn generate-sources -Pupdate-schemas-from-npm-artifact` before running the code generator.
- **DO NOT** edit `.github/agents/` directory - these contain instructions for other agents
- **DO NOT** modify `target/` directory - this contains build artifacts
- **DO NOT** edit `pom.xml` dependencies without careful consideration - this SDK has minimal dependencies by design
- **DO NOT** change the Jackson version without testing against all serialization patterns
-- **DO NOT** modify test snapshots in `target/copilot-sdk/test/snapshots/` - these come from upstream
+- **DO NOT** modify test snapshots in `target/copilot-sdk/test/snapshots/` - these come from reference implementation
- **DO NOT** alter the Eclipse formatter configuration in `pom.xml` without team consensus
- **DO NOT** remove or skip Checkstyle or Spotless checks
@@ -244,6 +246,18 @@ This SDK is designed to be **lightweight with minimal dependencies**:
5. Check for security vulnerabilities
6. Get team approval for non-trivial additions
+## Pre-commit Hooks and Formatting (Coding Agent)
+
+The repository has a pre-commit hook (`.githooks/pre-commit`) that is **automatically enabled** in the Copilot coding agent environment via `copilot-setup-steps.yml`. The hook runs `mvn spotless:check` on any commit that includes changes under `src/`.
+
+**If a commit fails due to the pre-commit hook:**
+
+1. Run `mvn spotless:apply` to auto-fix formatting issues.
+2. Re-stage the changed files with `git add -u`.
+3. Retry the commit.
+
+**Best practice:** Always run `mvn spotless:apply` before committing Java source changes to avoid hook failures in the first place. If you forget and the hook rejects the commit, follow the three steps above and continue.
+
## Commit and PR Guidelines
### Commit Messages
@@ -266,7 +280,7 @@ This SDK is designed to be **lightweight with minimal dependencies**:
- Include tests for new functionality
- Update documentation if adding/changing public APIs
- Reference related issues using `#issue-number`
-- For upstream merges, follow the `agentic-merge-upstream` skill workflow
+- For reference implementation merges, follow the `agentic-merge-reference-impl` skill workflow
## Development Workflow
@@ -287,12 +301,12 @@ The release process is automated via the `publish-maven.yml` GitHub Actions work
- Converts the `## [Unreleased]` section to `## [version] - date`
- Creates a new empty `## [Unreleased]` section at the top
- Updates version comparison links at the bottom of CHANGELOG.md
- - Injects the upstream SDK commit hash (from `.lastmerge`) as a `> **Upstream sync:**` blockquote in both the new `[Unreleased]` section and the released version section
+ - Injects the reference implementation SDK commit hash (from `.lastmerge`) as a `> **Reference implementation sync:**` blockquote in both the new `[Unreleased]` section and the released version section
-2. **Upstream Sync Tracking**: Each release records which commit from the official `github/copilot-sdk` it is synced to:
+2. **Reference Implementation Sync Tracking**: Each release records which commit from the official `github/copilot-sdk` it is synced to:
- The `.lastmerge` file is read during the release workflow
- The commit hash is injected into `CHANGELOG.md` under the release heading
- - Format: `> **Upstream sync:** [\`github/copilot-sdk@SHORT_HASH\`](link-to-commit)`
+ - Format: `> **Reference implementation sync:** [\`github/copilot-sdk@SHORT_HASH\`](link-to-commit)`
3. **Documentation Updates**: README.md and jbang-example.java are updated with the new version.
@@ -304,3 +318,4 @@ The release process is automated via the `publish-maven.yml` GitHub Actions work
5. **Rollback**: If the release fails, the documentation commit is automatically reverted
The workflow is triggered manually via workflow_dispatch with optional version parameters.
+
diff --git a/.github/prompts/agentic-merge-upstream.prompt.md b/.github/prompts/agentic-merge-reference-impl.prompt.md
similarity index 64%
rename from .github/prompts/agentic-merge-upstream.prompt.md
rename to .github/prompts/agentic-merge-reference-impl.prompt.md
index c1d2c54a0..88cc2bdfe 100644
--- a/.github/prompts/agentic-merge-upstream.prompt.md
+++ b/.github/prompts/agentic-merge-reference-impl.prompt.md
@@ -1,15 +1,52 @@
-# Merge Upstream SDK Changes
-
-You are an expert Java developer tasked with porting changes from the official Copilot SDK (primarily the .NET implementation) to this Java SDK.
+# Merge Reference Implementation SDK Changes
+
+You are an expert Java developer tasked with porting changes from the reference implementation of the Copilot SDK (primarily the .NET implementation) to this Java SDK.
+
+## ❌❌❌ ABSOLUTE PROHIBITION: DO NOT TOUCH GENERATED CODE ❌❌❌
+
+> ### 🚫 THE FILES UNDER `src/generated/java/` ARE FORBIDDEN 🚫
+>
+> **NEVER, UNDER ANY CIRCUMSTANCES, MODIFY ANY FILE IN `src/generated/java/`.**
+>
+> These files are AUTO-GENERATED by `scripts/codegen/java.ts` and MUST NOT be hand-edited.
+> They are regenerated automatically when the `@github/copilot` npm package version is updated.
+>
+> ❌ DO NOT edit `src/generated/java/**/*.java`
+> ❌ DO NOT create new files in `src/generated/java/`
+> ❌ DO NOT delete files from `src/generated/java/`
+> ❌ DO NOT "fix" or "improve" generated code — it will be overwritten
+>
+> **IF ANY CHANGE YOU NEED TO MAKE REQUIRES TOUCHING `src/generated/java/`:**
+>
+> 1. **STOP IMMEDIATELY** — do not make the change
+> 2. **FAIL the agentic sync** — do not attempt to work around this restriction
+> 3. **Push an explanatory commit** with a message such as:
+> ```
+> SYNC BLOCKED: Required change needs generated code update
+>
+> The reference implementation change '' requires updates
+> to the generated RPC/event types in src/generated/java/. These files
+> cannot be hand-edited — they must be regenerated.
+>
+> ACTION REQUIRED: Re-run the update-copilot-dependency.yml workflow
+> to update the @github/copilot npm package and regenerate the Java
+> source files before this sync can be completed.
+> ```
+> 4. **Document in the PR body** which reference implementation changes were blocked and why
+> 5. **Do NOT attempt to work around this restriction** by making equivalent changes elsewhere
+>
+> The correct way to update generated code is:
+> - Trigger the `update-copilot-dependency.yml` workflow with the new `@github/copilot` version
+> - That workflow updates `package.json`, regenerates all files in `src/generated/java/`, and opens a PR
## ⚠️ IMPORTANT: Java SDK Design Takes Priority
-**The current design and architecture of the Java SDK is the priority.** When porting changes from upstream:
+**The current design and architecture of the Java SDK is the priority.** When porting changes from the reference implementation:
-1. **Adapt, don't copy** - Translate upstream features to fit the Java SDK's existing patterns, naming conventions, and architecture
+1. **Adapt, don't copy** - Translate reference implementation features to fit the Java SDK's existing patterns, naming conventions, and architecture
2. **Preserve Java idioms** - The Java SDK should feel natural to Java developers, not like a C# port
3. **Maintain consistency** - New code must match the existing codebase style and structure
-4. **Evaluate before porting** - Not every upstream change needs to be ported; some may not be applicable or may conflict with Java SDK design decisions
+4. **Evaluate before porting** - Not every reference implementation change needs to be ported; some may not be applicable or may conflict with Java SDK design decisions
Before making any changes, **read and understand the existing Java SDK implementation** to ensure new code integrates seamlessly.
@@ -19,50 +56,50 @@ The `.github/scripts/` directory contains helper scripts that automate the repea
| Script | Purpose |
|---|---|
-| `.github/scripts/upstream-sync/merge-upstream-start.sh` | Creates branch, updates CLI, clones upstream, reads `.lastmerge`, prints commit summary |
-| `.github/scripts/upstream-sync/merge-upstream-diff.sh` | Detailed diff analysis grouped by area (`.NET src`, tests, snapshots, docs, etc.) |
-| `.github/scripts/upstream-sync/merge-upstream-finish.sh` | Runs format + test + build, updates `.lastmerge`, commits, pushes branch |
+| `.github/scripts/reference-impl-sync/merge-reference-impl-start.sh` | Creates branch, updates CLI, clones reference implementation, reads `.lastmerge`, prints commit summary |
+| `.github/scripts/reference-impl-sync/merge-reference-impl-diff.sh` | Detailed diff analysis grouped by area (`.NET src`, tests, snapshots, docs, etc.) |
+| `.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh` | Runs format + test + build, updates `.lastmerge`, commits, pushes branch |
| `.github/scripts/build/format-and-test.sh` | Standalone `spotless:apply` + `mvn clean verify` (useful during porting too) |
-All scripts write/read a `.merge-env` file (git-ignored) to share state (branch name, upstream dir, last-merge commit).
+All scripts write/read a `.merge-env` file (git-ignored) to share state (branch name, reference-impl dir, last-merge commit).
## Workflow Overview
-1. Run `./.github/scripts/upstream-sync/merge-upstream-start.sh` (creates branch, clones upstream, shows summary)
-2. Run `./.github/scripts/upstream-sync/merge-upstream-diff.sh` (analyze changes)
+1. Run `./.github/scripts/reference-impl-sync/merge-reference-impl-start.sh` (creates branch, clones reference implementation, shows summary)
+2. Run `./.github/scripts/reference-impl-sync/merge-reference-impl-diff.sh` (analyze changes)
3. Update README with minimum CLI version requirement
-4. Identify upstream changes to port
+4. Identify reference implementation changes to port
5. Apply changes to Java SDK (commit as you go)
-6. Port/adjust tests from upstream changes
+6. Port/adjust tests from reference implementation changes
7. Run `./.github/scripts/build/format-and-test.sh` frequently while porting
8. Build the package
-9. Update documentation (**required for every user-facing upstream change**)
-10. Run `./.github/scripts/upstream-sync/merge-upstream-finish.sh` (final test + push) and finalize Pull Request (see note below about coding agent vs. manual workflow)
+9. Update documentation (**required for every user-facing reference implementation change**)
+10. Run `./.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh` (final test + push) and finalize Pull Request (see note below about coding agent vs. manual workflow)
11. Perform final review before handing off
---
-## Step 1: Initialize Upstream Sync
+## Step 1: Initialize Reference Implementation Sync
-Run the start script to create a branch, update the CLI, clone the upstream repo, and see a summary of new commits:
+Run the start script to create a branch, update the CLI, clone the reference implementation repo, and see a summary of new commits:
```bash
-./.github/scripts/upstream-sync/merge-upstream-start.sh
+./.github/scripts/reference-impl-sync/merge-reference-impl-start.sh
```
This writes a `.merge-env` file used by the other scripts. It outputs:
- The branch name created
- The Copilot CLI version
-- The upstream dir path
-- A short log of upstream commits since `.lastmerge`
+- The reference-impl dir path
+- A short log of reference implementation commits since `.lastmerge`
-## Step 2: Analyze Upstream Changes
+## Step 2: Analyze reference implementation Changes
Run the diff script for a detailed breakdown by area:
```bash
-./.github/scripts/upstream-sync/merge-upstream-diff.sh # stat only
-./.github/scripts/upstream-sync/merge-upstream-diff.sh --full # full diffs
+./.github/scripts/reference-impl-sync/merge-reference-impl-diff.sh # stat only
+./.github/scripts/reference-impl-sync/merge-reference-impl-diff.sh --full # full diffs
```
The diff script groups changes into: .NET source, .NET tests, test snapshots, documentation, protocol/config, Go/Node.js/Python SDKs, and other files.
@@ -80,13 +117,13 @@ git commit -m "Update Copilot CLI minimum version requirement"
## Step 4: Identify Changes to Port
-Using the output from `merge-upstream-diff.sh`, focus on:
+Using the output from `merge-reference-impl-diff.sh`, focus on:
- `dotnet/src/` - Primary reference implementation
- `dotnet/test/` - Test cases to port
- `docs/` - Documentation updates
- `sdk-protocol-version.json` - Protocol version changes
-For each change in the upstream diff, determine:
+For each change in the reference implementation diff, determine:
1. **New Features**: New methods, classes, or capabilities added to the SDK
2. **Bug Fixes**: Corrections to existing functionality
@@ -96,12 +133,12 @@ For each change in the upstream diff, determine:
### Key Files to Compare
-| Upstream (.NET) | Java SDK Equivalent |
+| reference implementation (.NET) | Java SDK Equivalent |
|------------------------------------|--------------------------------------------------------|
| `dotnet/src/Client.cs` | `src/main/java/com/github/copilot/sdk/CopilotClient.java` |
| `dotnet/src/Session.cs` | `src/main/java/com/github/copilot/sdk/CopilotSession.java` |
| `dotnet/src/Types.cs` | `src/main/java/com/github/copilot/sdk/types/*.java` |
-| `dotnet/src/Generated/*.cs` | `src/main/java/com/github/copilot/sdk/types/*.java` |
+| `dotnet/src/Generated/*.cs` | ❌ **DO NOT TOUCH** `src/generated/java/**` — see top of this file |
| `dotnet/test/*.cs` | `src/test/java/com/github/copilot/sdk/*Test.java` |
| `docs/getting-started.md` | `README.md` and `src/site/markdown/*.md` |
| `docs/*.md` (new files) | `src/site/markdown/*.md` + update `src/site/site.xml` |
@@ -111,6 +148,10 @@ For each change in the upstream diff, determine:
## Step 5: Apply Changes to Java SDK
+> ### ❌❌❌ REMINDER: `src/generated/java/` IS FORBIDDEN ❌❌❌
+> Any change that requires modifying `src/generated/java/` MUST stop the sync.
+> See the **ABSOLUTE PROHIBITION** section at the top of this file for required actions.
+
When porting changes:
### ⚠️ Priority: Preserve Java SDK Design
@@ -121,7 +162,7 @@ Before modifying any code:
2. **Identify the Java equivalent approach** - Don't replicate C# patterns; find the idiomatic Java way
3. **Check for existing abstractions** - The Java SDK may already have mechanisms that differ from .NET
4. **Preserve backward compatibility** - Existing API signatures should not break unless absolutely necessary
-5. **When in doubt, match existing code** - Follow what's already in the Java SDK, not the upstream
+5. **When in doubt, match existing code** - Follow what's already in the Java SDK, not the reference implementation
### Commit Changes Incrementally
@@ -130,12 +171,12 @@ Before modifying any code:
```bash
# After porting a feature or fix, commit with a descriptive message
git add
-git commit -m "Port from upstream"
+git commit -m "Port from the reference implementation"
# Example commits:
-# git commit -m "Port new authentication flow from upstream"
-# git commit -m "Add new message types from upstream protocol update"
-# git commit -m "Port bug fix for session handling from upstream"
+# git commit -m "Port new authentication flow from the reference implementation"
+# git commit -m "Add new message types from the reference implementation protocol update"
+# git commit -m "Port bug fix for session handling from the reference implementation"
```
This creates a clear history of changes that can be reviewed in the Pull Request.
@@ -170,19 +211,19 @@ Follow the existing Java SDK patterns:
- Use Java records for DTOs where appropriate
- Follow the existing package structure under `com.github.copilot.sdk`
- Maintain backward compatibility when possible
-- **Match the style of surrounding code** - Consistency with existing code is more important than upstream patterns
+- **Match the style of surrounding code** - Consistency with existing code is more important than reference implementation patterns
- **Prefer existing abstractions** - If the Java SDK already solves a problem differently than .NET, keep the Java approach
## Step 6: Port Tests
-After porting implementation changes, **always check for new or updated tests** in the upstream repository:
+After porting implementation changes, **always check for new or updated tests** in the reference implementation repository:
### Check for New Tests
```bash
cd "$TEMP_DIR/copilot-sdk"
-git diff "$LAST_MERGE_COMMIT"..origin/main --stat -- dotnet/test/
-git diff "$LAST_MERGE_COMMIT"..origin/main --stat -- test/snapshots/
+git diff "$LAST_REFERENCE_IMPL_COMMIT"..origin/main --stat -- dotnet/test/
+git diff "$LAST_REFERENCE_IMPL_COMMIT"..origin/main --stat -- test/snapshots/
```
### Port Test Cases
@@ -196,7 +237,7 @@ For each new or modified test file in `dotnet/test/`:
### Test File Mapping
-| Upstream Test (.NET) | Java SDK Test |
+| reference implementation Test (.NET) | Java SDK Test |
|-----------------------------|--------------------------------------------------------|
| `dotnet/test/AskUserTests.cs` | `src/test/java/com/github/copilot/sdk/AskUserTest.java` |
| `dotnet/test/HooksTests.cs` | `src/test/java/com/github/copilot/sdk/HooksTest.java` |
@@ -205,11 +246,11 @@ For each new or modified test file in `dotnet/test/`:
### Test Snapshot Compatibility
-New test snapshots are stored in `test/snapshots/` in the upstream repository. These snapshots are automatically cloned during the Maven build process.
+New test snapshots are stored in `test/snapshots/` in the reference implementation repository. These snapshots are automatically cloned during the Maven build process.
If tests fail with errors like `TypeError: Cannot read properties of undefined`, the test harness may not yet support the new RPC methods. In this case:
-1. **Mark tests as `@Disabled`** with a clear reason (e.g., `@Disabled("Requires test harness update with X support - see upstream PR #NNN")`)
+1. **Mark tests as `@Disabled`** with a clear reason (e.g., `@Disabled("Requires test harness update with X support - see reference implementation PR #NNN")`)
2. **Document the dependency** in the test class Javadoc
3. **Enable tests later** once the harness is updated
@@ -266,7 +307,7 @@ Verify:
## Step 9: Update Documentation
-**Documentation is critical for new features.** Every new feature ported from upstream must be documented before the merge is complete.
+**Documentation is critical for new features.** Every new feature ported from the reference implementation must be documented before the merge is complete.
Review and complete this documentation checklist before proceeding to Step 10.
If you determine no docs changes are needed, document that decision and rationale in the PR body under a clear heading (for example, `Documentation Impact`).
@@ -328,31 +369,31 @@ Ensure consistency across all documentation files:
Run the finish script which updates `.lastmerge`, runs a final build, and pushes the branch:
```bash
-./.github/scripts/upstream-sync/merge-upstream-finish.sh # full format + test + push
-./.github/scripts/upstream-sync/merge-upstream-finish.sh --skip-tests # if tests already passed
+./.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh # full format + test + push
+./.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh --skip-tests # if tests already passed
```
### PR Handling: Coding Agent vs. Manual Workflow
-**If running as a Copilot coding agent** (triggered via GitHub issue assignment by the weekly sync workflow), a pull request has **already been created automatically** for you. Do NOT create a new one. Just push your commits to the current branch — the existing PR will be updated. Add the `upstream-sync` label to the existing PR by running this command in a terminal:
+**If running as a Copilot coding agent** (triggered via GitHub issue assignment by the weekly sync workflow), a pull request has **already been created automatically** for you. Do NOT create a new one. Just push your commits to the current branch — the existing PR will be updated. Add the `reference-impl-sync` label to the existing PR by running this command in a terminal:
```bash
-gh pr edit --add-label "upstream-sync"
+gh pr edit --add-label "reference-impl-sync"
```
-> **No-changes scenario (coding agent only):** If after analyzing the upstream diff there are no relevant changes to port to the Java SDK, push an empty commit with a message explaining why no changes were needed, so the PR reflects the analysis outcome. The repository maintainer will close the PR and issue manually.
+> **No-changes scenario (coding agent only):** If after analyzing the reference implementation diff there are no relevant changes to port to the Java SDK, push an empty commit with a message explaining why no changes were needed, so the PR reflects the analysis outcome. The repository maintainer will close the PR and issue manually.
**If running manually** (e.g., from VS Code via the reusable prompt), create the Pull Request using `gh` CLI or the GitHub MCP tool. Then add the label:
```bash
-gh pr create --base main --title "Merge upstream SDK changes (YYYY-MM-DD)" --body-file /dev/stdin <<< "$PR_BODY"
-gh pr edit --add-label "upstream-sync"
+gh pr create --base main --title "Merge reference implementation SDK changes (YYYY-MM-DD)" --body-file /dev/stdin <<< "$PR_BODY"
+gh pr edit --add-label "reference-impl-sync"
```
The PR body should include:
-1. **Title**: `Merge upstream SDK changes (YYYY-MM-DD)`
+1. **Title**: `Merge reference implementation SDK changes (YYYY-MM-DD)`
2. **Body** with:
- - Summary of upstream commits analyzed (with count and commit range)
+ - Summary of reference implementation commits analyzed (with count and commit range)
- Table of changes ported (commit hash + description)
- List of changes intentionally not ported (with reasons)
- Verification status (test count, build status)
@@ -360,13 +401,13 @@ The PR body should include:
### PR Body Template
```markdown
-## Upstream Merge
+## Reference Implementation Merge
Ports changes from the official Copilot SDK ([github/copilot-sdk](https://github.com/github/copilot-sdk)) since last merge (``→``).
-### Upstream commits analyzed (N commits)
+### Reference implementation commits analyzed (N commits)
-- Brief description of each upstream change and whether it was ported or not
+- Brief description of each reference implementation change and whether it was ported or not
### Changes ported
@@ -400,14 +441,15 @@ Before finishing:
## Checklist
+- [ ] ❌ **VERIFIED: No files in `src/generated/java/` were modified** (if any were needed, sync was stopped per ABSOLUTE PROHIBITION above)
- [ ] New branch created from `main`
- [ ] Copilot CLI updated to latest version
- [ ] README.md updated with minimum CLI version requirement
-- [ ] Upstream repository cloned
+- [ ] reference implementation repository cloned
- [ ] Diff analyzed between `.lastmerge` commit and HEAD
- [ ] New features/fixes identified
- [ ] Changes ported to Java SDK following conventions
-- [ ] **New/updated tests ported from upstream** (check `dotnet/test/` and `test/snapshots/`)
+- [ ] **New/updated tests ported from the reference implementation** (check `dotnet/test/` and `test/snapshots/`)
- [ ] Tests marked `@Disabled` if harness doesn't support new features yet
- [ ] Changes committed incrementally with descriptive messages
- [ ] `mvn test` passes
@@ -418,22 +460,24 @@ Before finishing:
- [ ] `src/site/markdown/documentation.md` updated for new basic usage
- [ ] `src/site/markdown/advanced.md` updated for new advanced features
- [ ] Javadoc added/updated for new public APIs
-- [ ] If no documentation files were changed for user-facing upstream changes, PR body explicitly explains why documentation changes were not needed
+- [ ] If no documentation files were changed for user-facing reference implementation changes, PR body explicitly explains why documentation changes were not needed
- [ ] `src/site/site.xml` updated if new documentation pages were added
- [ ] `.lastmerge` file updated with new commit hash
- [ ] Branch pushed to remote
- [ ] **Pull Request finalized** (coding agent: push to existing PR; manual: create via `mcp_github_create_pull_request`)
-- [ ] **`upstream-sync` label added** to the PR via `mcp_github_add_issue_labels`
+- [ ] **`reference-impl-sync` label added** to the PR via `mcp_github_add_issue_labels`
- [ ] PR URL provided to user
---
## Notes
-- The upstream SDK is at: `https://github.com/github/copilot-sdk.git`
+- ❌❌❌ **`src/generated/java/` IS FORBIDDEN** — NEVER modify generated files; re-run `update-copilot-dependency.yml` instead ❌❌❌
+- The reference implementation SDK is at: `https://github.com/github/copilot-sdk.git`
- Primary reference implementation is in `dotnet/` folder
- This Java SDK targets Java 17+
- Uses Jackson for JSON processing
- Uses JUnit 5 for testing
-- **Java SDK design decisions take precedence over upstream patterns**
-- **Adapt upstream changes to fit Java idioms, not the other way around**
+- **Java SDK design decisions take precedence over reference implementation patterns**
+- **Adapt reference implementation changes to fit Java idioms, not the other way around**
+
diff --git a/.github/prompts/coding-agent-merge-instructions.md b/.github/prompts/coding-agent-merge-instructions.md
deleted file mode 100644
index 1c18e1f5f..000000000
--- a/.github/prompts/coding-agent-merge-instructions.md
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-Follow the agentic-merge-upstream prompt at .github/prompts/agentic-merge-upstream.prompt.md
-to port upstream changes to the Java SDK.
-
-Use the utility scripts in .github/scripts/ subfolders for initialization, diffing, formatting, and testing.
-Commit changes incrementally. Update .lastmerge when done.
-
-IMPORTANT: A pull request has already been created automatically for you — do NOT create a new
-one. Push your commits to the current branch, and the existing PR will be updated.
-
-Add the 'upstream-sync' label to the existing PR by running this command in a terminal:
-
- gh pr edit --add-label "upstream-sync"
-
-If after analyzing the upstream diff there are no relevant changes to port to the Java SDK,
-push an empty commit with a message explaining why no changes were needed, so the PR reflects
-the analysis outcome. The repository maintainer will close the PR and issue manually.
diff --git a/.github/prompts/coding-agent-merge-reference-impl-instructions.md b/.github/prompts/coding-agent-merge-reference-impl-instructions.md
new file mode 100644
index 000000000..c93af9a1a
--- /dev/null
+++ b/.github/prompts/coding-agent-merge-reference-impl-instructions.md
@@ -0,0 +1,33 @@
+
+
+
+Follow the agentic-merge-reference-impl prompt at .github/prompts/agentic-merge-reference-impl.prompt.md
+to port reference implementation changes to the Java SDK.
+
+Use the utility scripts in .github/scripts/ subfolders for initialization, diffing, formatting, and testing.
+Commit changes incrementally. Update .lastmerge when done.
+
+IMPORTANT: A pull request has already been created automatically for you — do NOT create a new
+one. Push your commits to the current branch, and the existing PR will be updated.
+
+Add the 'reference-impl-sync' label to the existing PR by running this command in a terminal:
+
+ gh pr edit --add-label "reference-impl-sync"
+
+If after analyzing the reference implementation diff there are no relevant changes to port to the Java SDK,
+push an empty commit with a message explaining why no changes were needed, so the PR reflects
+the analysis outcome. The repository maintainer will close the PR and issue manually.
+
+❌❌❌ ABSOLUTE PROHIBITION ❌❌❌
+
+NEVER MODIFY ANY FILE UNDER src/generated/java/ — THESE FILES ARE AUTO-GENERATED AND FORBIDDEN.
+
+If any change requires modifying src/generated/java/:
+1. STOP IMMEDIATELY — do not make the change
+2. FAIL the sync with an explanatory commit message
+3. Instruct the maintainer to re-run update-copilot-dependency.yml to regenerate these files
+
+See the ABSOLUTE PROHIBITION section in .github/prompts/agentic-merge-reference-impl.prompt.md
+for the full required procedure and commit message template.
+
+❌❌❌ END ABSOLUTE PROHIBITION ❌❌❌
diff --git a/.github/scripts/upstream-sync/merge-upstream-diff.sh b/.github/scripts/reference-impl-sync/merge-reference-impl-diff.sh
similarity index 86%
rename from .github/scripts/upstream-sync/merge-upstream-diff.sh
rename to .github/scripts/reference-impl-sync/merge-reference-impl-diff.sh
index ee61b6ffc..8ff870103 100755
--- a/.github/scripts/upstream-sync/merge-upstream-diff.sh
+++ b/.github/scripts/reference-impl-sync/merge-reference-impl-diff.sh
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────────
-# merge-upstream-diff.sh
+# merge-reference-impl-diff.sh
#
-# Generates a detailed diff analysis of upstream changes since
+# Generates a detailed diff analysis of reference implementation changes since
# the last merge, grouped by area of interest:
# • .NET source (primary reference)
# • .NET tests
@@ -10,10 +10,10 @@
# • Documentation
# • Protocol / config files
#
-# Usage: ./.github/scripts/upstream-sync/merge-upstream-diff.sh [--full]
+# Usage: ./.github/scripts/reference-impl-sync/merge-reference-impl-diff.sh [--full]
# --full Show actual diffs, not just stats
#
-# Requires: .merge-env written by merge-upstream-start.sh
+# Requires: .merge-env written by merge-reference-impl-start.sh
# ──────────────────────────────────────────────────────────────
set -euo pipefail
@@ -21,7 +21,7 @@ ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
ENV_FILE="$ROOT_DIR/.merge-env"
if [[ ! -f "$ENV_FILE" ]]; then
- echo "❌ $ENV_FILE not found. Run ./.github/scripts/upstream-sync/merge-upstream-start.sh first."
+ echo "❌ $ENV_FILE not found. Run ./.github/scripts/reference-impl-sync/merge-reference-impl-start.sh first."
exit 1
fi
@@ -33,13 +33,13 @@ if [[ "${1:-}" == "--full" ]]; then
SHOW_FULL=true
fi
-cd "$UPSTREAM_DIR"
+cd "$REFERENCE_IMPL_DIR"
git fetch origin main 2>/dev/null
RANGE="$LAST_MERGE_COMMIT..origin/main"
echo "════════════════════════════════════════════════════════════"
-echo " Upstream diff analysis: $RANGE"
+echo " Reference implementation diff analysis: $RANGE"
echo "════════════════════════════════════════════════════════════"
# ── Commit log ────────────────────────────────────────────────
@@ -82,5 +82,5 @@ section "Other files" "README.md" "CONTRIBUTING.md" "SECURIT
echo "════════════════════════════════════════════════════════════"
echo " To see full diffs: $0 --full"
echo " To see a specific path:"
-echo " cd $UPSTREAM_DIR && git diff $RANGE -- "
+echo " cd $REFERENCE_IMPL_DIR && git diff $RANGE -- "
echo "════════════════════════════════════════════════════════════"
diff --git a/.github/scripts/upstream-sync/merge-upstream-finish.sh b/.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh
similarity index 72%
rename from .github/scripts/upstream-sync/merge-upstream-finish.sh
rename to .github/scripts/reference-impl-sync/merge-reference-impl-finish.sh
index 1663ef259..480d43bc0 100755
--- a/.github/scripts/upstream-sync/merge-upstream-finish.sh
+++ b/.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh
@@ -1,17 +1,17 @@
#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────────
-# merge-upstream-finish.sh
+# merge-reference-impl-finish.sh
#
-# Finalises an upstream merge:
+# Finalises a reference implementation merge:
# 1. Runs format + test + build (via format-and-test.sh)
-# 2. Updates .lastmerge to upstream HEAD
+# 2. Updates .lastmerge to reference implementation HEAD
# 3. Commits the .lastmerge update
# 4. Pushes the branch to origin
#
-# Usage: ./.github/scripts/upstream-sync/merge-upstream-finish.sh
-# ./.github/scripts/upstream-sync/merge-upstream-finish.sh --skip-tests
+# Usage: ./.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh
+# ./.github/scripts/reference-impl-sync/merge-reference-impl-finish.sh --skip-tests
#
-# Requires: .merge-env written by merge-upstream-start.sh
+# Requires: .merge-env written by merge-reference-impl-start.sh
# ──────────────────────────────────────────────────────────────
set -euo pipefail
@@ -19,7 +19,7 @@ ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
ENV_FILE="$ROOT_DIR/.merge-env"
if [[ ! -f "$ENV_FILE" ]]; then
- echo "❌ $ENV_FILE not found. Run ./.github/scripts/upstream-sync/merge-upstream-start.sh first."
+ echo "❌ $ENV_FILE not found. Run ./.github/scripts/reference-impl-sync/merge-reference-impl-start.sh first."
exit 1
fi
@@ -45,7 +45,7 @@ fi
# ── 2. Update .lastmerge ─────────────────────────────────────
echo "▸ Updating .lastmerge…"
-NEW_COMMIT=$(cd "$UPSTREAM_DIR" && git rev-parse origin/main)
+NEW_COMMIT=$(cd "$REFERENCE_IMPL_DIR" && git rev-parse origin/main)
echo "$NEW_COMMIT" > "$ROOT_DIR/.lastmerge"
git add .lastmerge
@@ -59,5 +59,5 @@ echo ""
echo "✅ Branch pushed. Next step:"
echo " Create a Pull Request (base: main, head: $BRANCH_NAME)"
echo ""
-echo " Suggested title: Merge upstream SDK changes ($(date +%Y-%m-%d))"
-echo " Don't forget to add the 'upstream-sync' label."
+echo " Suggested title: Merge reference implementation SDK changes ($(date +%Y-%m-%d))"
+echo " Don't forget to add the 'reference-impl-sync' label."
diff --git a/.github/scripts/upstream-sync/merge-upstream-start.sh b/.github/scripts/reference-impl-sync/merge-reference-impl-start.sh
similarity index 68%
rename from .github/scripts/upstream-sync/merge-upstream-start.sh
rename to .github/scripts/reference-impl-sync/merge-reference-impl-start.sh
index 755361cd1..76408e4ec 100755
--- a/.github/scripts/upstream-sync/merge-upstream-start.sh
+++ b/.github/scripts/reference-impl-sync/merge-reference-impl-start.sh
@@ -1,15 +1,15 @@
#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────────
-# merge-upstream-start.sh
+# merge-reference-impl-start.sh
#
-# Prepares the workspace for an upstream merge:
+# Prepares the workspace for a reference implementation merge:
# 1. Creates a dated branch from main
# 2. Updates Copilot CLI and records the new version
-# 3. Clones the upstream copilot-sdk repo into a temp dir
+# 3. Clones the reference implementation copilot-sdk repo into a temp dir
# 4. Reads .lastmerge and prints a short summary of new commits
#
-# Usage: ./.github/scripts/upstream-sync/merge-upstream-start.sh
-# Output: Exports UPSTREAM_DIR and LAST_MERGE_COMMIT to a
+# Usage: ./.github/scripts/reference-impl-sync/merge-reference-impl-start.sh
+# Output: Exports REFERENCE_IMPL_DIR and LAST_MERGE_COMMIT to a
# .merge-env file so other scripts can source it.
# ──────────────────────────────────────────────────────────────
set -euo pipefail
@@ -17,7 +17,7 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
cd "$ROOT_DIR"
-UPSTREAM_REPO="https://github.com/github/copilot-sdk.git"
+REFERENCE_IMPL_REPO="https://github.com/github/copilot-sdk.git"
ENV_FILE="$ROOT_DIR/.merge-env"
# ── 1. Create branch (or reuse existing) ─────────────────────
@@ -33,7 +33,7 @@ else
echo "▸ Ensuring main is up to date…"
git pull origin main
- BRANCH_NAME="merge-upstream-$(date +%Y%m%d)"
+ BRANCH_NAME="merge-reference-impl-$(date +%Y%m%d)"
echo "▸ Creating branch: $BRANCH_NAME"
git checkout -b "$BRANCH_NAME"
fi
@@ -49,11 +49,11 @@ else
CLI_VERSION="UNKNOWN"
fi
-# ── 3. Clone upstream ────────────────────────────────────────
+# ── 3. Clone reference implementation ────────────────────────────────────────
TEMP_DIR=$(mktemp -d)
-UPSTREAM_DIR="$TEMP_DIR/copilot-sdk"
-echo "▸ Cloning upstream into $UPSTREAM_DIR …"
-git clone --depth=200 "$UPSTREAM_REPO" "$UPSTREAM_DIR"
+REFERENCE_IMPL_DIR="$TEMP_DIR/copilot-sdk"
+echo "▸ Cloning reference implementation into $REFERENCE_IMPL_DIR …"
+git clone --depth=200 "$REFERENCE_IMPL_REPO" "$REFERENCE_IMPL_DIR"
# ── 4. Read last merge commit ────────────────────────────────
if [[ ! -f "$ROOT_DIR/.lastmerge" ]]; then
@@ -61,21 +61,21 @@ if [[ ! -f "$ROOT_DIR/.lastmerge" ]]; then
exit 1
fi
LAST_MERGE_COMMIT=$(tr -d '[:space:]' < "$ROOT_DIR/.lastmerge")
-echo "▸ Last merged upstream commit: $LAST_MERGE_COMMIT"
+echo "▸ Last merged reference implementation commit: $LAST_MERGE_COMMIT"
# Quick summary
echo ""
-echo "── Upstream commits since last merge ──"
-(cd "$UPSTREAM_DIR" && git fetch origin main && \
+echo "── Reference implementation commits since last merge ──"
+(cd "$REFERENCE_IMPL_DIR" && git fetch origin main && \
git log --oneline "$LAST_MERGE_COMMIT"..origin/main) || \
echo " (could not generate log – the commit may have been rebased)"
echo ""
# ── 5. Write env file for other scripts ──────────────────────
cat > "$ENV_FILE" < [upstream-hash]
+# Usage: ./update-changelog.sh [reference-impl-hash]
# Example: ./update-changelog.sh 1.0.8
# Example: ./update-changelog.sh 1.0.8 05e3c46c8c23130c9c064dc43d00ec78f7a75eab
if [ -z "$1" ]; then
echo "Error: Version argument required"
- echo "Usage: $0 [upstream-hash]"
+ echo "Usage: $0 [reference-impl-hash]"
exit 1
fi
VERSION="$1"
-UPSTREAM_HASH="${2:-}"
+REFERENCE_IMPL_HASH="${2:-}"
CHANGELOG_FILE="${CHANGELOG_FILE:-CHANGELOG.md}"
RELEASE_DATE=$(date +%Y-%m-%d)
echo "Updating CHANGELOG.md for version ${VERSION} (${RELEASE_DATE})"
-if [ -n "$UPSTREAM_HASH" ]; then
- echo " Upstream SDK sync: ${UPSTREAM_HASH:0:7}"
+if [ -n "$REFERENCE_IMPL_HASH" ]; then
+ echo " Reference implementation SDK sync: ${REFERENCE_IMPL_HASH:0:7}"
fi
# Check if CHANGELOG.md exists
@@ -38,7 +38,7 @@ fi
TEMP_FILE=$(mktemp)
# Process the CHANGELOG
-awk -v version="$VERSION" -v date="$RELEASE_DATE" -v upstream_hash="$UPSTREAM_HASH" '
+awk -v version="$VERSION" -v date="$RELEASE_DATE" -v REFERENCE_IMPL_HASH="$REFERENCE_IMPL_HASH" '
BEGIN {
unreleased_found = 0
content_found = 0
@@ -65,26 +65,26 @@ links_section && repo_url == "" && /^\[[0-9]+\.[0-9]+\.[0-9]+(-java\.[0-9]+)?\]:
if (!unreleased_found) {
print "## [Unreleased]"
print ""
- if (upstream_hash != "") {
- short_hash = substr(upstream_hash, 1, 7)
- print "> **Upstream sync:** [`github/copilot-sdk@" short_hash "`](https://github.com/github/copilot-sdk/commit/" upstream_hash ")"
+ if (REFERENCE_IMPL_HASH != "") {
+ short_hash = substr(REFERENCE_IMPL_HASH, 1, 7)
+ print "> **Reference implementation sync:** [`github/copilot-sdk@" short_hash "`](https://github.com/github/copilot-sdk/commit/" REFERENCE_IMPL_HASH ")"
print ""
}
print "## [" version "] - " date
- if (upstream_hash != "") {
+ if (REFERENCE_IMPL_HASH != "") {
print ""
- print "> **Upstream sync:** [`github/copilot-sdk@" short_hash "`](https://github.com/github/copilot-sdk/commit/" upstream_hash ")"
+ print "> **Reference implementation sync:** [`github/copilot-sdk@" short_hash "`](https://github.com/github/copilot-sdk/commit/" REFERENCE_IMPL_HASH ")"
}
unreleased_found = 1
- skip_old_upstream = 1
+ skip_old_reference_impl = 1
next
}
}
-# Skip the old upstream sync line and surrounding blank lines from the previous [Unreleased] section
-skip_old_upstream && /^[[:space:]]*$/ { next }
-skip_old_upstream && /^> \*\*Upstream sync:\*\*/ { next }
-skip_old_upstream && !/^[[:space:]]*$/ && !/^> \*\*Upstream sync:\*\*/ { skip_old_upstream = 0 }
+# Skip the old Reference implementation sync line and surrounding blank lines from the previous [Unreleased] section
+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]+)?\]:/ {
@@ -119,3 +119,4 @@ echo "✓ CHANGELOG.md updated successfully"
echo " - Added version ${VERSION} with date ${RELEASE_DATE}"
echo " - Created new [Unreleased] section"
echo " - Updated version comparison links"
+
diff --git a/.github/skills/agentic-merge-reference-impl/SKILL.md b/.github/skills/agentic-merge-reference-impl/SKILL.md
new file mode 100644
index 000000000..43addbac4
--- /dev/null
+++ b/.github/skills/agentic-merge-reference-impl/SKILL.md
@@ -0,0 +1,7 @@
+---
+name: agentic-merge-reference-impl
+description: Merge reference implementation changes from the official Copilot SDK into this Java SDK.
+license: MIT
+---
+
+Follow instructions in the [agentic-merge-reference-impl prompt](../../prompts/agentic-merge-reference-impl.prompt.md) to merge reference implementation changes from the official Copilot SDK into this Java SDK.
diff --git a/.github/skills/agentic-merge-upstream/SKILL.md b/.github/skills/agentic-merge-upstream/SKILL.md
deleted file mode 100644
index 14301c27d..000000000
--- a/.github/skills/agentic-merge-upstream/SKILL.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-name: agentic-merge-upstream
-description: Merge upstream changes from the official Copilot SDK into this Java SDK.
-license: MIT
----
-
-Follow instructions in the [agentic-merge-upstream prompt](../../prompts/agentic-merge-upstream.prompt.md) to merge upstream changes from the official Copilot SDK into this Java SDK.
\ No newline at end of file
diff --git a/.github/templates/index.html b/.github/templates/index.html
index 9af01dded..d273ad074 100644
--- a/.github/templates/index.html
+++ b/.github/templates/index.html
@@ -65,8 +65,8 @@
Available Versions
-
- ⚠️ Disclaimer: This is the official Java SDK for GitHub Copilot. This repository treats the official .NET and nodejs SDKs for GitHub Copilot as reference implementations. These SDKS are all officially supported as GitHub open source projects. The Java implementation follows the backward compatibility guarantees offered by the reference implementations. As such this implementation may introduce breaking changes, according to the policy declared by the reference implementations. Use at your own risk.
+
+ ℹ️ Public Preview: This is the official Java SDK for GitHub Copilot. This repository treats the official .NET and Node.js SDKs for GitHub Copilot as reference implementations. These SDKs are all officially supported as GitHub open source projects. The Java implementation follows the backward compatibility guarantees offered by the reference implementations. While in public preview, minor breaking changes may still occur between releases.
diff --git a/.github/workflows/codegen-check.yml b/.github/workflows/codegen-check.yml
new file mode 100644
index 000000000..675629c1e
--- /dev/null
+++ b/.github/workflows/codegen-check.yml
@@ -0,0 +1,44 @@
+name: "Codegen Check"
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+ paths:
+ - 'scripts/codegen/**'
+ - 'src/generated/java/**'
+ - '.github/workflows/codegen-check.yml'
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+jobs:
+ check:
+ name: "Verify generated files are up-to-date"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+
+ - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
+ with:
+ node-version: 22
+
+ - name: Install codegen dependencies
+ working-directory: ./scripts/codegen
+ run: npm ci
+
+ - name: Run codegen
+ working-directory: ./scripts/codegen
+ run: npm run generate
+
+ - name: Check for uncommitted changes
+ run: |
+ if [ -n "$(git status --porcelain)" ]; then
+ echo "::error::Generated files are out of date. Run 'cd scripts/codegen && npm run generate' and commit the changes."
+ git diff --stat
+ git diff
+ exit 1
+ fi
+ echo "✅ Generated files are up-to-date"
diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml
index 6a0cdec5b..145629457 100644
--- a/.github/workflows/copilot-setup-steps.yml
+++ b/.github/workflows/copilot-setup-steps.yml
@@ -41,6 +41,10 @@ jobs:
distribution: 'temurin'
cache: 'maven'
+ # Enable repository pre-commit hooks (including Spotless checks for relevant source changes)
+ - name: Enable pre-commit hooks
+ run: git config core.hooksPath .githooks
+
# Verify installations
- name: Verify tool installations
run: |
@@ -50,4 +54,6 @@ jobs:
java -version
gh --version
gh aw version
+ echo "--- Git hooks path ---"
+ git config core.hooksPath
echo "✅ All tools installed successfully"
diff --git a/.github/workflows/notes.template b/.github/workflows/notes.template
index 0fd7af642..9c148cdf1 100644
--- a/.github/workflows/notes.template
+++ b/.github/workflows/notes.template
@@ -1,6 +1,6 @@
# Installation
-⚠️ **Disclaimer:** This is the official Java SDK for GitHub Copilot. This repository treats the official .NET and nodejs SDKs for GitHub Copilot as reference implementations. These SDKS are all officially supported as GitHub open source projects. The Java implementation follows the backward compatibility guarantees offered by the reference implementations. As such this implementation may introduce breaking changes, according to the policy declared by the reference implementations. Use at your own risk.
+ℹ️ **Public Preview:** This is the official Java SDK for GitHub Copilot. This repository treats the official .NET and Node.js SDKs for GitHub Copilot as reference implementations. These SDKs are all officially supported as GitHub open source projects. The Java implementation follows the backward compatibility guarantees offered by the reference implementations. While in public preview, minor breaking changes may still occur between releases.
⚠️ **Artifact versioning plan:** Releases of this implementation track releases of the reference implementation. For each release of the reference implementation, there may follow a corresponding relase of this implementation with the same number as the reference implementation. Release identifiers of the reference implementation are in the form `vMaj.Min.Micro`. For example v0.1.32. The corresponding maven version for the release will be `Maj.Min.Micro-java.N`, where `Maj`, `Min` and `Micro` are the corresponding numbers for the reference impementation release, and `N` is a monotonically increasing sequence number starting with 0 for each release. See the corrseponding architectural decision record for more information in the `docs/adr` directory of the source code.
diff --git a/.github/workflows/publish-maven.yml b/.github/workflows/publish-maven.yml
index a7bb58f01..540da6ae7 100644
--- a/.github/workflows/publish-maven.yml
+++ b/.github/workflows/publish-maven.yml
@@ -108,21 +108,26 @@ jobs:
run: |
VERSION="${{ steps.versions.outputs.release_version }}"
- # Read the upstream SDK commit hash that this release is synced to
- UPSTREAM_HASH=$(cat .lastmerge)
- UPSTREAM_SHORT="${UPSTREAM_HASH:0:7}"
- UPSTREAM_URL="https://github.com/github/copilot-sdk/commit/${UPSTREAM_HASH}"
- echo "Upstream SDK sync: ${UPSTREAM_SHORT} (${UPSTREAM_URL})"
+ # Read the reference implementation SDK commit hash that this release is synced to
+ REFERENCE_IMPL_HASH=$(cat .lastmerge)
+ REFERENCE_IMPL_SHORT="${REFERENCE_IMPL_HASH:0:7}"
+ REFERENCE_IMPL_URL="https://github.com/github/copilot-sdk/commit/${REFERENCE_IMPL_HASH}"
+ echo "Reference implementation SDK sync: ${REFERENCE_IMPL_SHORT} (${REFERENCE_IMPL_URL})"
- # Update CHANGELOG.md with release version and upstream sync hash
- ./.github/scripts/release/update-changelog.sh "${VERSION}" "${UPSTREAM_HASH}"
+ # 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 and 0.1.32-java.0)
sed -i "s|[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\.[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\.[0-9][0-9]*\)\{0,1\}|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\.[0-9][0-9]*\)\{0,1\}-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\.[0-9][0-9]*\)\{0,1\}|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 \
@@ -206,7 +211,7 @@ jobs:
# Build the gh release command
GH_ARGS=("${CURRENT_TAG}")
- GH_ARGS+=("--title" "Copilot Java SDK ${VERSION}")
+ GH_ARGS+=("--title" "GitHub Copilot SDK for Java ${VERSION}")
GH_ARGS+=("--notes" "${RELEASE_NOTES}")
GH_ARGS+=("--generate-notes")
@@ -244,3 +249,4 @@ jobs:
-f publish_as_latest=true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
diff --git a/.github/workflows/update-copilot-dependency.yml b/.github/workflows/update-copilot-dependency.yml
new file mode 100644
index 000000000..bb86bfd83
--- /dev/null
+++ b/.github/workflows/update-copilot-dependency.yml
@@ -0,0 +1,144 @@
+name: "Update @github/copilot Dependency"
+
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Target version of @github/copilot (e.g. 1.0.24)'
+ required: true
+ type: string
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ update:
+ name: "Update @github/copilot to ${{ inputs.version }}"
+ runs-on: ubuntu-latest
+ steps:
+ - name: Validate version input
+ env:
+ VERSION: ${{ inputs.version }}
+ run: |
+ if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9._-]+)?$ ]]; then
+ echo "::error::Invalid version format '$VERSION'. Expected semver (e.g. 1.0.24)."
+ exit 1
+ fi
+
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+
+ - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
+ with:
+ node-version: 22
+
+ - name: Update @github/copilot in scripts/codegen
+ env:
+ VERSION: ${{ inputs.version }}
+ working-directory: ./scripts/codegen
+ # npm install updates package.json and package-lock.json to the new
+ # version; npm ci (below) then does a clean, reproducible install from
+ # the updated lock file. Both steps are required: npm install alone
+ # leaves leftover packages, while npm ci alone cannot change the pinned
+ # version in the lock file.
+ run: npm install "@github/copilot@$VERSION"
+
+ - name: Install codegen dependencies
+ working-directory: ./scripts/codegen
+ run: npm ci
+
+ - name: Run codegen
+ working-directory: ./scripts/codegen
+ run: npm run generate
+
+ - uses: ./.github/actions/setup-copilot
+
+ - name: Verify generated code against schema
+ env:
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ run: |
+ cat > /tmp/verify-codegen-prompt.txt << 'PROMPT_EOF'
+ You are running inside the copilot-sdk-java repository.
+ The code generator has just run and produced Java source files under
+ src/generated/java/com/github/copilot/sdk/generated/rpc/.
+
+ Your task is to spot-check the generated API classes against the source
+ JSON schema to verify the generator produced correct output.
+
+ **Critical constraint: Do NOT modify any files. This is a read-only
+ verification. Use only read/inspect operations.**
+
+ **Steps:**
+ 1. Read the API schema at:
+ scripts/codegen/node_modules/@github/copilot/schemas/api.schema.json
+
+ 2. Pick these 3 generated API classes to verify:
+ - src/generated/java/com/github/copilot/sdk/generated/rpc/SessionToolsApi.java
+ - src/generated/java/com/github/copilot/sdk/generated/rpc/SessionUiApi.java
+ - src/generated/java/com/github/copilot/sdk/generated/rpc/SessionPermissionsApi.java
+
+ 3. For each class, verify:
+ - Every RPC method in the schema's corresponding namespace has a
+ matching Java method in the generated class
+ - Parameter record fields match the JSON schema property names
+ - Java types are consistent with JSON schema types (String for
+ "string", Integer/int for "integer", Boolean/boolean for
+ "boolean", List for "array", Map or a generated class for
+ "object", etc.)
+ - No extra methods exist in the Java class that are not in the
+ schema
+
+ 4. Print a summary table of what was checked and the result for each
+ method.
+
+ 5. If ANY mismatch is found, print the details and exit with a
+ non-zero code.
+ If all checks pass, print "Schema verification passed" and exit 0.
+ PROMPT_EOF
+
+ copilot --yolo --prompt "$(cat /tmp/verify-codegen-prompt.txt)"
+
+ - name: Create pull request
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ VERSION: ${{ inputs.version }}
+ run: |
+ BRANCH="update-copilot-$VERSION"
+ git config user.name "github-actions[bot]"
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
+
+ if git rev-parse --verify "origin/$BRANCH" >/dev/null 2>&1; then
+ git checkout "$BRANCH"
+ git reset --hard HEAD
+ else
+ git checkout -b "$BRANCH"
+ fi
+
+ git add -A
+
+ if git diff --cached --quiet; then
+ echo "No changes detected; skipping commit and PR creation."
+ exit 0
+ fi
+
+ git commit -m "Update @github/copilot to $VERSION
+
+ - Updated @github/copilot in scripts/codegen
+ - Re-ran Java code generator"
+ git push origin "$BRANCH" --force-with-lease
+
+ if gh pr view "$BRANCH" >/dev/null 2>&1; then
+ echo "Pull request for branch '$BRANCH' already exists; updated branch only."
+ else
+ gh pr create \
+ --title "Update @github/copilot to $VERSION" \
+ --body "Automated update of \`@github/copilot\` to version \`$VERSION\`.
+
+ ### Changes
+ - Updated \`@github/copilot\` in \`scripts/codegen/package.json\`
+ - Re-ran Java code generator (\`scripts/codegen\`)
+
+ > Created by the **Update @github/copilot Dependency** workflow." \
+ --base main \
+ --head "$BRANCH"
+ fi
diff --git a/.github/workflows/weekly-reference-impl-sync.lock.yml b/.github/workflows/weekly-reference-impl-sync.lock.yml
new file mode 100644
index 000000000..49bfd3ac0
--- /dev/null
+++ b/.github/workflows/weekly-reference-impl-sync.lock.yml
@@ -0,0 +1,1266 @@
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"54f733578d3011b148c7aff09a0e72085d787a777a996488f08aeffdf52ec93b","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"}]}
+# ___ _ _
+# / _ \ | | (_)
+# | |_| | __ _ ___ _ __ | |_ _ ___
+# | _ |/ _` |/ _ \ '_ \| __| |/ __|
+# | | | | (_| | __/ | | | |_| | (__
+# \_| |_/\__, |\___|_| |_|\__|_|\___|
+# __/ |
+# _ _ |___/
+# | | | | / _| |
+# | | | | ___ _ __ _ __| |_| | _____ ____
+# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
+# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
+# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
+#
+# This file was automatically generated by gh-aw (v0.68.3). DO NOT EDIT.
+#
+# To update this file, edit the corresponding .md file and run:
+# gh aw compile
+# Not all edits will cause changes to this file.
+#
+# For more information: https://github.github.com/gh-aw/introduction/overview/
+#
+# Weekly reference implementation sync workflow. Checks for new commits in the official
+# Copilot SDK (github/copilot-sdk) and assigns to Copilot to port changes.
+#
+# Secrets used:
+# - COPILOT_GITHUB_TOKEN
+# - GH_AW_AGENT_TOKEN
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.20
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.20
+# - ghcr.io/github/gh-aw-mcpg:v0.2.19
+# - ghcr.io/github/github-mcp-server:v0.32.0
+# - node:lts-alpine
+
+name: "Weekly Reference Implementation Sync"
+"on":
+ schedule:
+ - cron: "40 11 * * 0"
+ # Friendly format: weekly (scattered)
+ workflow_dispatch:
+ inputs:
+ aw_context:
+ default: ""
+ description: Agent caller context (used internally by Agentic Workflows).
+ required: false
+ type: string
+
+permissions: {}
+
+concurrency:
+ group: "gh-aw-${{ github.workflow }}"
+
+run-name: "Weekly Reference Implementation Sync"
+
+jobs:
+ activation:
+ runs-on: ubuntu-slim
+ permissions:
+ actions: read
+ contents: read
+ outputs:
+ comment_id: ""
+ comment_repo: ""
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
+ model: ${{ steps.generate_aw_info.outputs.model }}
+ secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'auto' }}
+ GH_AW_INFO_VERSION: "1.0.21"
+ GH_AW_INFO_AGENT_VERSION: "1.0.21"
+ GH_AW_INFO_CLI_VERSION: "v0.68.3"
+ GH_AW_INFO_WORKFLOW_NAME: "Weekly Reference Implementation Sync"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "false"
+ GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
+ GH_AW_INFO_FIREWALL_ENABLED: "true"
+ GH_AW_INFO_AWF_VERSION: "v0.25.20"
+ GH_AW_INFO_AWMG_VERSION: ""
+ GH_AW_INFO_FIREWALL_TYPE: "squid"
+ GH_AW_COMPILED_STRICT: "true"
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
+ await main(core, context);
+ - name: Validate COPILOT_GITHUB_TOKEN secret
+ id: validate-secret
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
+ env:
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ - name: Checkout .github and .agents folders
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ sparse-checkout: |
+ .github
+ .agents
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_WORKFLOW_FILE: "weekly-reference-impl-sync.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_COMPILED_VERSION: "v0.68.3"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs');
+ await main();
+ - name: Create prompt with built-in context
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
+ GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
+ GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
+ GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ # poutine:ignore untrusted_checkout_exec
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
+ {
+ cat << 'GH_AW_PROMPT_9a049ffd9b204d97_EOF'
+
+ GH_AW_PROMPT_9a049ffd9b204d97_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_9a049ffd9b204d97_EOF'
+
+ Tools: add_comment(max:10), create_issue, close_issue(max:10), assign_to_agent, missing_tool, missing_data, noop
+
+
+ The following GitHub context information is available for this workflow:
+ {{#if __GH_AW_GITHUB_ACTOR__ }}
+ - **actor**: __GH_AW_GITHUB_ACTOR__
+ {{/if}}
+ {{#if __GH_AW_GITHUB_REPOSITORY__ }}
+ - **repository**: __GH_AW_GITHUB_REPOSITORY__
+ {{/if}}
+ {{#if __GH_AW_GITHUB_WORKSPACE__ }}
+ - **workspace**: __GH_AW_GITHUB_WORKSPACE__
+ {{/if}}
+ {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }}
+ - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__
+ {{/if}}
+ {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }}
+ - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__
+ {{/if}}
+ {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }}
+ - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__
+ {{/if}}
+ {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }}
+ - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__
+ {{/if}}
+ {{#if __GH_AW_GITHUB_RUN_ID__ }}
+ - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
+ {{/if}}
+
+
+ GH_AW_PROMPT_9a049ffd9b204d97_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_9a049ffd9b204d97_EOF'
+
+ {{#runtime-import .github/workflows/weekly-reference-impl-sync.md}}
+ GH_AW_PROMPT_9a049ffd9b204d97_EOF
+ } > "$GH_AW_PROMPT"
+ - name: Interpolate variables and render templates
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs');
+ await main();
+ - name: Substitute placeholders
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
+ GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
+ GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
+ GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+
+ const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
+
+ // Call the substitution function
+ return await substitutePlaceholders({
+ file: process.env.GH_AW_PROMPT,
+ substitutions: {
+ GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
+ GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID,
+ GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER,
+ GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER,
+ GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER,
+ GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
+ GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE
+ }
+ });
+ - name: Validate prompt placeholders
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
+ - name: Print prompt
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
+ - name: Upload activation artifact
+ if: success()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: activation
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/github_rate_limits.jsonl
+ if-no-files-found: ignore
+ retention-days: 1
+
+ agent:
+ needs: activation
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ issues: read
+ concurrency:
+ group: "gh-aw-copilot-${{ github.workflow }}"
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ""
+ GH_AW_ASSETS_BRANCH: ""
+ GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ GH_AW_WORKFLOW_ID_SANITIZED: weeklyreferenceimplsync
+ outputs:
+ agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }}
+ checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
+ has_patch: ${{ steps.collect_output.outputs.has_patch }}
+ inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }}
+ model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }}
+ output: ${{ steps.collect_output.outputs.output }}
+ output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - name: Create gh-aw temp directory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
+ - name: Configure gh CLI for GitHub Enterprise
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
+ env:
+ GH_TOKEN: ${{ github.token }}
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Checkout PR branch
+ id: checkout-pr
+ if: |
+ github.event.pull_request || github.event.issue.pull_request
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
+ await main();
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.21
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.20
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.20 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20 ghcr.io/github/gh-aw-firewall/squid:0.25.20 ghcr.io/github/gh-aw-mcpg:v0.2.19 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
+ - name: Write Safe Outputs Config
+ run: |
+ 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_2a317c5680ea1925_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":{"assignees":["copilot-swe-agent"],"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_2a317c5680ea1925_EOF
+ - name: Write Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
+ {
+ "description_suffixes": {
+ "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: *.",
+ "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. Assignees [\"copilot-swe-agent\"] will be automatically assigned."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "add_comment": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "item_number": {
+ "issueOrPRNumber": true
+ },
+ "reply_to_id": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ }
+ }
+ },
+ "assign_to_agent": {
+ "defaultMax": 1,
+ "fields": {
+ "agent": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "issue_number": {
+ "issueNumberOrTemporaryId": true
+ },
+ "pull_number": {
+ "optionalPositiveInteger": true
+ },
+ "pull_request_repo": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ }
+ },
+ "customValidation": "requiresOneOf:issue_number,pull_number"
+ },
+ "close_issue": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "issue_number": {
+ "optionalPositiveInteger": true
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ }
+ }
+ },
+ "create_issue": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "labels": {
+ "type": "array",
+ "itemType": "string",
+ "itemSanitize": true,
+ "itemMaxLength": 128
+ },
+ "parent": {
+ "issueOrPRNumber": true
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "temporary_id": {
+ "type": "string"
+ },
+ "title": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 1024
+ }
+ }
+ }
+ }
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs');
+ await main();
+ - name: Generate Safe Outputs MCP Server Config
+ id: safe-outputs-config
+ run: |
+ # Generate a secure random API key (360 bits of entropy, 40+ chars)
+ # Mask immediately to prevent timing vulnerabilities
+ API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${API_KEY}"
+
+ PORT=3001
+
+ # Set outputs for next steps
+ {
+ echo "safe_outputs_api_key=${API_KEY}"
+ echo "safe_outputs_port=${PORT}"
+ } >> "$GITHUB_OUTPUT"
+
+ echo "Safe Outputs MCP server will run on port ${PORT}"
+
+ - name: Start Safe Outputs MCP HTTP Server
+ id: safe-outputs-start
+ env:
+ DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ run: |
+ # Environment variables are set above to prevent template injection
+ export DEBUG
+ export GH_AW_SAFE_OUTPUTS
+ export GH_AW_SAFE_OUTPUTS_PORT
+ export GH_AW_SAFE_OUTPUTS_API_KEY
+ export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
+ export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
+ export GH_AW_MCP_LOG_DIR
+
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
+
+ - name: Start MCP Gateway
+ id: start-mcp-gateway
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ run: |
+ set -eo pipefail
+ mkdir -p /tmp/gh-aw/mcp-config
+
+ # Export gateway environment variables for MCP config and gateway script
+ export MCP_GATEWAY_PORT="80"
+ export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${MCP_GATEWAY_API_KEY}"
+ export MCP_GATEWAY_API_KEY
+ export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
+ mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
+ export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
+ export DEBUG="*"
+
+ export GH_AW_ENGINE="copilot"
+ 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_ee30822602a32ffa_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh"
+ {
+ "mcpServers": {
+ "github": {
+ "type": "stdio",
+ "container": "ghcr.io/github/github-mcp-server:v0.32.0",
+ "env": {
+ "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
+ "GITHUB_READ_ONLY": "1",
+ "GITHUB_TOOLSETS": "context,repos,issues"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
+ }
+ },
+ "safeoutputs": {
+ "type": "http",
+ "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
+ "headers": {
+ "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
+ }
+ },
+ "gateway": {
+ "port": $MCP_GATEWAY_PORT,
+ "domain": "${MCP_GATEWAY_DOMAIN}",
+ "apiKey": "${MCP_GATEWAY_API_KEY}",
+ "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
+ }
+ }
+ GH_AW_MCP_CONFIG_ee30822602a32ffa_EOF
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: activation
+ path: /tmp/gh-aw
+ - name: Clean git credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Execute GitHub Copilot CLI
+ id: agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ touch /tmp/gh-aw/agent-step-summary.md
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ # shellcheck disable=SC1003
+ sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-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' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.20 --skip-pull --enable-api-proxy \
+ -- /bin/bash -c 'node ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ env:
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }}
+ GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
+ GH_AW_PHASE: agent
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_VERSION: v0.68.3
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Detect Copilot errors
+ id: detect-copilot-errors
+ if: always()
+ continue-on-error: true
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - name: Copy Copilot session state files to logs
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
+ - name: Stop MCP Gateway
+ if: always()
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
+ - name: Redact secrets in logs
+ if: always()
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs');
+ await main();
+ env:
+ GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
+ SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Append agent step summary
+ if: always()
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
+ if: always()
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
+ - name: Ingest agent output
+ id: collect_output
+ if: always()
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ 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 }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs');
+ await main();
+ - name: Parse agent logs for step summary
+ if: always()
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs');
+ await main();
+ - name: Parse MCP Gateway logs for step summary
+ if: always()
+ id: parse-mcp-gateway
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs');
+ await main();
+ - name: Print firewall logs
+ if: always()
+ continue-on-error: true
+ env:
+ AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
+ run: |
+ # Fix permissions on firewall logs so they can be uploaded as artifacts
+ # AWF runs with sudo, creating files owned by root
+ sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true
+ # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
+ if command -v awf &> /dev/null; then
+ awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
+ else
+ echo 'AWF binary not installed, skipping firewall log summary'
+ fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
+ - name: Upload agent artifacts
+ if: always()
+ continue-on-error: true
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: agent
+ path: |
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
+ /tmp/gh-aw/mcp-logs/
+ /tmp/gh-aw/agent_usage.json
+ /tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/aw-*.patch
+ /tmp/gh-aw/aw-*.bundle
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ if-no-files-found: ignore
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - detection
+ - safe_outputs
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ pull-requests: write
+ concurrency:
+ group: "gh-aw-conclusion-weekly-reference-impl-sync"
+ cancel-in-progress: false
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_NOOP_MAX: "1"
+ GH_AW_WORKFLOW_NAME: "Weekly Reference Implementation Sync"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "false"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
+ await main();
+ - name: Log detection run
+ id: detection_runs
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_WORKFLOW_NAME: "Weekly Reference Implementation Sync"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Weekly Reference Implementation Sync"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Weekly Reference Implementation Sync"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
+ await main();
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_WORKFLOW_NAME: "Weekly Reference Implementation Sync"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "weekly-reference-impl-sync"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ASSIGNMENT_ERRORS: ${{ needs.safe_outputs.outputs.assign_to_agent_assignment_errors }}
+ GH_AW_ASSIGNMENT_ERROR_COUNT: ${{ needs.safe_outputs.outputs.assign_to_agent_assignment_error_count }}
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_TIMEOUT_MINUTES: "20"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
+ await main();
+
+ detection:
+ needs:
+ - activation
+ - agent
+ if: >
+ always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Checkout repository for patch context
+ if: needs.agent.outputs.has_patch == 'true'
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ # --- Threat Detection ---
+ - name: Clean stale firewall files from agent artifact
+ run: |
+ rm -rf /tmp/gh-aw/sandbox/firewall/logs
+ rm -rf /tmp/gh-aw/sandbox/firewall/audit
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.20 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20 ghcr.io/github/gh-aw-firewall/squid:0.25.20
+ - name: Check if detection needed
+ id: detection_guard
+ if: always()
+ env:
+ OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ run: |
+ if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
+ echo "run_detection=true" >> "$GITHUB_OUTPUT"
+ echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
+ else
+ echo "run_detection=false" >> "$GITHUB_OUTPUT"
+ echo "Detection skipped: no agent outputs or patches to analyze"
+ fi
+ - name: Clear MCP configuration for detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ rm -f /tmp/gh-aw/mcp-config/mcp-servers.json
+ rm -f /home/runner/.copilot/mcp-config.json
+ rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
+ - name: Prepare threat detection files
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
+ cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
+ cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
+ for f in /tmp/gh-aw/aw-*.patch; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ for f in /tmp/gh-aw/aw-*.bundle; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ echo "Prepared threat detection files:"
+ ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ - name: Setup threat detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ WORKFLOW_NAME: "Weekly Reference Implementation Sync"
+ WORKFLOW_DESCRIPTION: "Weekly reference implementation sync workflow. Checks for new commits in the official\nCopilot SDK (github/copilot-sdk) and assigns to Copilot to port changes."
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs');
+ await main();
+ - name: Ensure threat-detection directory and log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection
+ touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.21
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.20
+ - name: Execute GitHub Copilot CLI
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ id: detection_agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ touch /tmp/gh-aw/agent-step-summary.md
+ (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
+ # shellcheck disable=SC1003
+ sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,telemetry.enterprise.githubcopilot.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.20 --skip-pull --enable-api-proxy \
+ -- /bin/bash -c 'node ${RUNNER_TEMP}/gh-aw/actions/copilot_driver.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
+ env:
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }}
+ GH_AW_PHASE: detection
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_VERSION: v0.68.3
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Upload threat detection log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: detection
+ path: /tmp/gh-aw/threat-detection/detection.log
+ if-no-files-found: ignore
+ - name: Parse and conclude threat detection
+ id: detection_conclusion
+ if: always()
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
+ GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs');
+ await main();
+
+ safe_outputs:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ pull-requests: write
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/weekly-reference-impl-sync"
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ GH_AW_WORKFLOW_ID: "weekly-reference-impl-sync"
+ GH_AW_WORKFLOW_NAME: "Weekly Reference Implementation Sync"
+ outputs:
+ assign_to_agent_assigned: ${{ steps.process_safe_outputs.outputs.assign_to_agent_assigned }}
+ assign_to_agent_assignment_error_count: ${{ steps.process_safe_outputs.outputs.assign_to_agent_assignment_error_count }}
+ assign_to_agent_assignment_errors: ${{ steps.process_safe_outputs.outputs.assign_to_agent_assignment_errors }}
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }}
+ comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
+ created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@ba90f2186d7ad780ec640f364005fa24e797b360 # v0.68.3
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Configure GH_HOST for enterprise compatibility
+ id: ghes-host-config
+ shell: bash
+ run: |
+ # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
+ # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
+ GH_HOST="${GITHUB_SERVER_URL#https://}"
+ GH_HOST="${GH_HOST#http://}"
+ echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9
+ env:
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ 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\":{\"assignees\":[\"copilot-swe-agent\"],\"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 }}
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs');
+ await main();
+ - name: Upload Safe Outputs Items
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: safe-outputs-items
+ path: |
+ /tmp/gh-aw/safe-output-items.jsonl
+ /tmp/gh-aw/temporary-id-map.json
+ if-no-files-found: ignore
+
diff --git a/.github/workflows/weekly-reference-impl-sync.md b/.github/workflows/weekly-reference-impl-sync.md
new file mode 100644
index 000000000..1d1f9a4fd
--- /dev/null
+++ b/.github/workflows/weekly-reference-impl-sync.md
@@ -0,0 +1,117 @@
+---
+description: |
+ Weekly reference implementation sync workflow. Checks for new commits in the official
+ Copilot SDK (github/copilot-sdk) and assigns to Copilot to port changes.
+
+on:
+ schedule: weekly
+ workflow_dispatch:
+
+permissions:
+ contents: read
+ actions: read
+ issues: read
+
+network:
+ allowed:
+ - defaults
+ - github
+
+tools:
+ github:
+ toolsets: [context, repos, issues]
+
+safe-outputs:
+ create-issue:
+ title-prefix: "[reference-impl-sync] "
+ assignees: [copilot-swe-agent]
+ labels: [reference-impl-sync]
+ expires: 6
+ close-issue:
+ required-labels: [reference-impl-sync]
+ target: "*"
+ max: 10
+ add-comment:
+ target: "*"
+ max: 10
+ assign-to-agent:
+ name: "copilot"
+ model: "claude-opus-4.6"
+ target: "*"
+ noop:
+ report-as-issue: false
+---
+# Weekly Reference Implementation Sync
+
+You are an automation agent that detects new reference implementation changes and creates GitHub issues. You do **NOT** perform any code merges, edits, or pushes. Do **NOT** invoke any skills (especially `agentic-merge-reference-impl`). Your only job is to check for changes and use safe-output tools to create or close issues.
+
+## Instructions
+
+Follow these steps exactly:
+
+### Step 1: Read `.lastmerge`
+
+Read the file `.lastmerge` in the repository root. It contains the SHA of the last reference implementation commit that was merged into this Java SDK.
+
+### Step 2: Check for reference implementation changes
+
+Clone the reference implementation repository and compare commits:
+
+```bash
+LAST_MERGE=$(cat .lastmerge)
+git clone --quiet https://github.com/github/copilot-sdk.git /tmp/gh-aw/agent/reference-impl
+cd /tmp/gh-aw/agent/reference-impl
+REFERENCE_IMPL_HEAD=$(git rev-parse HEAD)
+```
+
+If `LAST_MERGE` equals `REFERENCE_IMPL_HEAD`, there are **no new changes**. Go to Step 3a.
+
+If they differ, count the new commits and generate a summary:
+
+```bash
+COMMIT_COUNT=$(git rev-list --count "$LAST_MERGE".."$REFERENCE_IMPL_HEAD")
+SUMMARY=$(git log --oneline "$LAST_MERGE".."$REFERENCE_IMPL_HEAD" | head -20)
+```
+
+Go to Step 3b.
+
+### Step 3a: No changes detected
+
+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.
+
+### 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:
+ - **Title:** `Reference Implementation sync: new commits ()`
+ - **Body:** Include the following information:
+ ```
+ ## Automated Reference Implementation Sync
+
+ There are **** new commits in the [official Copilot SDK](https://github.com/github/copilot-sdk) since the last merge.
+
+ - **Last merged commit:** [``](https://github.com/github/copilot-sdk/commit/)
+ - **Reference Implementation HEAD:** [``](https://github.com/github/copilot-sdk/commit/)
+
+ ### Recent reference implementation commits
+
+ ```
+
+ ```
+
+ ### Instructions
+
+ Follow the [agentic-merge-reference-impl](.github/prompts/agentic-merge-reference-impl.prompt.md) prompt to port these changes to the Java SDK.
+ ```
+4. After creating the issue, use the `assign_to_agent` safe-output tool to assign Copilot to the newly created issue.
+
+## Important constraints
+
+- **Do NOT edit any files**, create branches, or push code.
+- **Do NOT invoke any skills** such as `agentic-merge-agentic-merge-reference-impl` or `commit-as-pull-request`.
+- **Do NOT attempt to merge or port reference implementation changes.** That is done by a separate agent that picks up the issue you create.
+- You **MUST** call at least one safe-output tool (`create_issue`, `close_issue`, `noop`, etc.) before finishing.
diff --git a/.github/workflows/weekly-upstream-sync.yml b/.github/workflows/weekly-reference-impl-sync.yml
similarity index 66%
rename from .github/workflows/weekly-upstream-sync.yml
rename to .github/workflows/weekly-reference-impl-sync.yml
index 5281fa032..aa3fc971a 100644
--- a/.github/workflows/weekly-upstream-sync.yml
+++ b/.github/workflows/weekly-reference-impl-sync.yml
@@ -1,4 +1,4 @@
-name: "Weekly Upstream Sync"
+name: "Weekly Reference Implementation Sync"
on:
schedule:
@@ -19,39 +19,39 @@ env:
jobs:
check-and-sync:
- name: "Check upstream & trigger Copilot merge"
+ 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 upstream changes
+ - 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/upstream
- cd /tmp/upstream
+ git clone --quiet https://github.com/github/copilot-sdk.git /tmp/reference-impl
+ cd /tmp/reference-impl
- UPSTREAM_HEAD=$(git rev-parse HEAD)
- echo "Upstream HEAD: $UPSTREAM_HEAD"
+ REFERENCE_IMPL_HEAD=$(git rev-parse HEAD)
+ echo "Reference implementation HEAD: $REFERENCE_IMPL_HEAD"
echo "last_merge=$LAST_MERGE" >> "$GITHUB_OUTPUT"
- if [ "$LAST_MERGE" = "$UPSTREAM_HEAD" ]; then
- echo "No new upstream changes since last merge."
+ 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".."$UPSTREAM_HEAD")
- echo "Found $COMMIT_COUNT new upstream commits."
+ 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 "upstream_head=$UPSTREAM_HEAD" >> "$GITHUB_OUTPUT"
+ echo "reference_impl_head=$REFERENCE_IMPL_HEAD" >> "$GITHUB_OUTPUT"
# Generate a short summary of changes
- SUMMARY=$(git log --oneline "$LAST_MERGE".."$UPSTREAM_HEAD" | head -20)
+ SUMMARY=$(git log --oneline "$LAST_MERGE".."$REFERENCE_IMPL_HEAD" | head -20)
{
echo "summary<> "$GITHUB_OUTPUT"
fi
- - name: Close previous upstream-sync issues
+ - name: Close previous reference-impl-sync issues
if: steps.check.outputs.has_changes == 'true'
run: |
- # Find all open issues with the upstream-sync label
+ # Find all open issues with the reference-impl-sync label
OPEN_ISSUES=$(gh issue list \
--repo "${{ github.repository }}" \
- --label "upstream-sync" \
+ --label "reference-impl-sync" \
--state open \
--json number \
--jq '.[].number')
@@ -74,27 +74,27 @@ jobs:
echo "Closing superseded issue #${ISSUE_NUM}"
gh issue comment "$ISSUE_NUM" \
--repo "${{ github.repository }}" \
- --body "Superseded by a newer upstream sync issue. Closing this one."
+ --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 upstream-sync issues (no changes)
+ - 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 "upstream-sync" \
+ --label "reference-impl-sync" \
--state open \
--json number \
--jq '.[].number')
for ISSUE_NUM in $OPEN_ISSUES; do
- echo "Closing stale issue #${ISSUE_NUM} — upstream is up to date"
+ echo "Closing stale issue #${ISSUE_NUM} — reference implementation is up to date"
gh issue comment "$ISSUE_NUM" \
--repo "${{ github.repository }}" \
- --body "No new upstream changes detected. The Java SDK is up to date. Closing."
+ --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"
@@ -108,18 +108,18 @@ jobs:
run: |
COMMIT_COUNT="${{ steps.check.outputs.commit_count }}"
LAST_MERGE="${{ steps.check.outputs.last_merge }}"
- UPSTREAM_HEAD="${{ steps.check.outputs.upstream_head }}"
+ REFERENCE_IMPL_HEAD="${{ steps.check.outputs.reference_impl_head }}"
DATE=$(date -u +"%Y-%m-%d")
REPO="${{ github.repository }}"
- BODY="## Automated Upstream Sync
+ 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})
- - **Upstream HEAD:** [\`${UPSTREAM_HEAD}\`](https://github.com/github/copilot-sdk/commit/${UPSTREAM_HEAD})
+ - **Reference implementation HEAD:** [\`${REFERENCE_IMPL_HEAD}\`](https://github.com/github/copilot-sdk/commit/${REFERENCE_IMPL_HEAD})
- ### Recent upstream commits
+ ### Recent reference implementation commits
\`\`\`
${SUMMARY}
@@ -127,14 +127,14 @@ jobs:
### Instructions
- Follow the [agentic-merge-upstream](.github/prompts/agentic-merge-upstream.prompt.md) prompt to port these changes to the Java SDK."
+ 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 and assign to Copilot coding agent
ISSUE_URL=$(gh issue create \
--repo "$REPO" \
- --title "Upstream sync: ${COMMIT_COUNT} new commits (${DATE})" \
+ --title "Reference implementation sync: ${COMMIT_COUNT} new commits (${DATE})" \
--body "$BODY" \
- --label "upstream-sync" \
+ --label "reference-impl-sync" \
--assignee "copilot-swe-agent")
echo "issue_url=$ISSUE_URL" >> "$GITHUB_OUTPUT"
@@ -148,25 +148,25 @@ jobs:
HAS_CHANGES="${{ steps.check.outputs.has_changes }}"
COMMIT_COUNT="${{ steps.check.outputs.commit_count }}"
LAST_MERGE="${{ steps.check.outputs.last_merge }}"
- UPSTREAM_HEAD="${{ steps.check.outputs.upstream_head }}"
+ REFERENCE_IMPL_HEAD="${{ steps.check.outputs.reference_impl_head }}"
ISSUE_URL="${{ steps.create-issue.outputs.issue_url }}"
{
- echo "## Weekly Upstream Sync"
+ echo "## Weekly Reference Implementation Sync"
echo ""
if [ "$HAS_CHANGES" = "true" ]; then
- echo "### ✅ New upstream changes detected"
+ echo "### ✅ New reference implementation changes detected"
echo ""
echo "| | |"
echo "|---|---|"
echo "| **New commits** | ${COMMIT_COUNT} |"
echo "| **Last merged** | \`${LAST_MERGE:0:12}\` |"
- echo "| **Upstream HEAD** | \`${UPSTREAM_HEAD: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 upstream commits"
+ echo "### Recent reference implementation commits"
echo ""
echo '```'
echo "$SUMMARY"
@@ -174,7 +174,7 @@ jobs:
else
echo "### ⏭️ No changes"
echo ""
- echo "The Java SDK is already up to date with the upstream Copilot SDK."
+ 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
diff --git a/.github/workflows/weekly-upstream-sync.lock.yml b/.github/workflows/weekly-upstream-sync.lock.yml
deleted file mode 100644
index c2bceee20..000000000
--- a/.github/workflows/weekly-upstream-sync.lock.yml
+++ /dev/null
@@ -1,1239 +0,0 @@
-#
-# ___ _ _
-# / _ \ | | (_)
-# | |_| | __ _ ___ _ __ | |_ _ ___
-# | _ |/ _` |/ _ \ '_ \| __| |/ __|
-# | | | | (_| | __/ | | | |_| | (__
-# \_| |_/\__, |\___|_| |_|\__|_|\___|
-# __/ |
-# _ _ |___/
-# | | | | / _| |
-# | | | | ___ _ __ _ __| |_| | _____ ____
-# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
-# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
-# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
-#
-# This file was automatically generated by gh-aw (v0.51.6). DO NOT EDIT.
-#
-# To update this file, edit the corresponding .md file and run:
-# gh aw compile
-# Not all edits will cause changes to this file.
-#
-# For more information: https://github.github.com/gh-aw/introduction/overview/
-#
-# Weekly upstream sync workflow. Checks for new commits in the official
-# Copilot SDK (github/copilot-sdk) and assigns to Copilot to port changes.
-#
-# gh-aw-metadata: {"schema_version":"v1","frontmatter_hash":"fc14b09206c7aeafcd52c843adce996a1c14cf15875f9b647ef71f631b3b296e","compiler_version":"v0.51.6"}
-
-name: "Weekly Upstream Sync Agentic Workflow"
-"on":
- schedule:
- - cron: "39 8 * * 2"
- # Friendly format: weekly (scattered)
- workflow_dispatch:
-
-permissions: {}
-
-concurrency:
- group: "gh-aw-${{ github.workflow }}"
-
-run-name: "Weekly Upstream Sync Agentic Workflow"
-
-jobs:
- activation:
- runs-on: ubuntu-slim
- permissions:
- contents: read
- outputs:
- comment_id: ""
- comment_repo: ""
- model: ${{ steps.generate_aw_info.outputs.model }}
- secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
- steps:
- - name: Setup Scripts
- uses: github/gh-aw/actions/setup@33cd6c7f1fee588654ef19def2e6a4174be66197 # v0.51.6
- with:
- destination: /opt/gh-aw/actions
- - name: Generate agentic run info
- id: generate_aw_info
- env:
- GH_AW_INFO_ENGINE_ID: "copilot"
- GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
- GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }}
- GH_AW_INFO_VERSION: ""
- GH_AW_INFO_AGENT_VERSION: "0.0.420"
- GH_AW_INFO_CLI_VERSION: "v0.51.6"
- GH_AW_INFO_WORKFLOW_NAME: "Weekly Upstream Sync Agentic Workflow"
- GH_AW_INFO_EXPERIMENTAL: "false"
- GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
- GH_AW_INFO_STAGED: "false"
- GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]'
- GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.23.0"
- GH_AW_INFO_AWMG_VERSION: ""
- GH_AW_INFO_FIREWALL_TYPE: "squid"
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
- await main(core, context);
- - name: Validate COPILOT_GITHUB_TOKEN secret
- id: validate-secret
- run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
- env:
- COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- - name: Checkout .github and .agents folders
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- sparse-checkout: |
- .github
- .agents
- sparse-checkout-cone-mode: true
- fetch-depth: 1
- persist-credentials: false
- - name: Check workflow file timestamps
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_WORKFLOW_FILE: "weekly-upstream-sync.lock.yml"
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs');
- await main();
- - name: Create prompt with built-in context
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
- GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
- GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
- GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
- {
- cat << 'GH_AW_PROMPT_EOF'
-
- GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_EOF'
-
- Tools: add_comment, create_issue, close_issue, assign_to_agent, missing_tool, missing_data, noop
-
-
- The following GitHub context information is available for this workflow:
- {{#if __GH_AW_GITHUB_ACTOR__ }}
- - **actor**: __GH_AW_GITHUB_ACTOR__
- {{/if}}
- {{#if __GH_AW_GITHUB_REPOSITORY__ }}
- - **repository**: __GH_AW_GITHUB_REPOSITORY__
- {{/if}}
- {{#if __GH_AW_GITHUB_WORKSPACE__ }}
- - **workspace**: __GH_AW_GITHUB_WORKSPACE__
- {{/if}}
- {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }}
- - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__
- {{/if}}
- {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }}
- - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__
- {{/if}}
- {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }}
- - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__
- {{/if}}
- {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }}
- - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__
- {{/if}}
- {{#if __GH_AW_GITHUB_RUN_ID__ }}
- - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
- {{/if}}
-
-
- GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF'
-
- GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF'
- {{#runtime-import .github/workflows/weekly-upstream-sync.md}}
- GH_AW_PROMPT_EOF
- } > "$GH_AW_PROMPT"
- - name: Interpolate variables and render templates
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs');
- await main();
- - name: Substitute placeholders
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_GITHUB_ACTOR: ${{ github.actor }}
- GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
- GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
- GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
- GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
- GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
- GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
- GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
-
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
-
- // Call the substitution function
- return await substitutePlaceholders({
- file: process.env.GH_AW_PROMPT,
- substitutions: {
- GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
- GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID,
- GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER,
- GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER,
- GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER,
- GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
- GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
- GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE
- }
- });
- - name: Validate prompt placeholders
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh
- - name: Print prompt
- env:
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- run: bash /opt/gh-aw/actions/print_prompt_summary.sh
- - name: Upload activation artifact
- if: success()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- with:
- name: activation
- path: |
- /tmp/gh-aw/aw_info.json
- /tmp/gh-aw/aw-prompts/prompt.txt
- retention-days: 1
-
- agent:
- needs: activation
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- issues: read
- concurrency:
- group: "gh-aw-copilot-${{ github.workflow }}"
- env:
- DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
- GH_AW_ASSETS_ALLOWED_EXTS: ""
- GH_AW_ASSETS_BRANCH: ""
- GH_AW_ASSETS_MAX_SIZE_KB: 0
- GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_WORKFLOW_ID_SANITIZED: weeklyupstreamsync
- outputs:
- checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
- detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
- detection_success: ${{ steps.detection_conclusion.outputs.success }}
- has_patch: ${{ steps.collect_output.outputs.has_patch }}
- model: ${{ needs.activation.outputs.model }}
- output: ${{ steps.collect_output.outputs.output }}
- output_types: ${{ steps.collect_output.outputs.output_types }}
- steps:
- - name: Setup Scripts
- uses: github/gh-aw/actions/setup@33cd6c7f1fee588654ef19def2e6a4174be66197 # v0.51.6
- with:
- destination: /opt/gh-aw/actions
- - name: Checkout repository
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- with:
- persist-credentials: false
- - name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
- - name: Configure Git credentials
- env:
- REPO_NAME: ${{ github.repository }}
- SERVER_URL: ${{ github.server_url }}
- run: |
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
- git config --global user.name "github-actions[bot]"
- git config --global am.keepcr true
- # Re-authenticate git with GitHub token
- SERVER_URL_STRIPPED="${SERVER_URL#https://}"
- git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
- echo "Git configured with standard GitHub Actions identity"
- - name: Checkout PR branch
- id: checkout-pr
- if: |
- (github.event.pull_request) || (github.event.issue.pull_request)
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs');
- await main();
- - name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.420
- - name: Install awf binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0
- - name: Determine automatic lockdown mode for GitHub MCP Server
- id: determine-automatic-lockdown
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- with:
- script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
- await determineAutomaticLockdown(github, context, core);
- - name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.6 ghcr.io/github/github-mcp-server:v0.31.0 node:lts-alpine
- - name: Write Safe Outputs Config
- run: |
- mkdir -p /opt/gh-aw/safeoutputs
- mkdir -p /tmp/gh-aw/safeoutputs
- mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
- {"add_comment":{"max":10,"target":"*"},"assign_to_agent":{"default_agent":"copilot","max":1,"target":"*"},"close_issue":{"max":10,"required_labels":["upstream-sync"],"target":"*"},"create_issue":{"expires":144,"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
- GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
- {
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[upstream-sync] \". Labels [upstream-sync] will be automatically added. Assignees [copilot] will be automatically assigned.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
- },
- "labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
- },
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 8 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,8}$",
- "type": "string"
- },
- "title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
- }
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
- },
- "name": "create_issue"
- },
- {
- "description": "Close a GitHub issue with a closing comment. You can and should always add a comment when closing an issue to explain the action or provide context. This tool is ONLY for closing issues - use update_issue if you need to change the title, body, labels, or other metadata without closing. Use close_issue when work is complete, the issue is no longer relevant, or it's a duplicate. The closing comment should explain the resolution or reason for closing. If the issue is already closed, a comment will still be posted. CONSTRAINTS: Maximum 10 issue(s) can be closed. Target: *.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Closing comment explaining why the issue is being closed and summarizing any resolution, workaround, or conclusion.",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to close. This is the numeric ID from the GitHub URL (e.g., 901 in github.com/owner/repo/issues/901). If omitted, closes the issue that triggered this workflow (requires an issue event trigger).",
- "type": [
- "number",
- "string"
- ]
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "close_issue"
- },
- {
- "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. IMPORTANT: Comments are subject to validation constraints enforced by the MCP server - maximum 65536 characters for the complete comment (including footer which is added automatically), 10 mentions (@username), and 50 links. Exceeding these limits will result in an immediate error with specific guidance. NOTE: By default, this tool requires discussions:write permission. If your GitHub App lacks Discussions permission, set 'discussions: false' in the workflow's safe-outputs.add-comment configuration to exclude this permission. CONSTRAINTS: Maximum 10 comment(s) can be added. Target: *.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation. CONSTRAINTS: The complete comment (your body text + automatically added footer) must not exceed 65536 characters total. Maximum 10 mentions (@username), maximum 50 links (http/https URLs). A footer (~200-500 characters) is automatically appended with workflow attribution, so leave adequate space. If these limits are exceeded, the tool call will fail with a detailed error message indicating which constraint was violated.",
- "type": "string"
- },
- "item_number": {
- "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool auto-targets the issue, PR, or discussion that triggered this workflow. Auto-targeting only works for issue, pull_request, discussion, and comment event triggers — it does NOT work for schedule, workflow_dispatch, push, or workflow_run triggers. For those trigger types, always provide item_number explicitly, or the comment will be silently discarded.",
- "type": "number"
- }
- },
- "required": [
- "body"
- ],
- "type": "object"
- },
- "name": "add_comment"
- },
- {
- "description": "Assign the GitHub Copilot coding agent to work on an issue or pull request. The agent will analyze the issue/PR and attempt to implement a solution, creating a pull request when complete. Use this to delegate coding tasks to Copilot. Example usage: assign_to_agent(issue_number=123, agent=\"copilot\") or assign_to_agent(pull_number=456, agent=\"copilot\", pull_request_repo=\"owner/repo\") CONSTRAINTS: Maximum 1 issue(s) can be assigned to agent.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "agent": {
- "description": "Agent identifier to assign. Defaults to 'copilot' (the Copilot coding agent) if not specified.",
- "type": "string"
- },
- "issue_number": {
- "description": "Issue number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 234 in github.com/owner/repo/issues/234). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from an issue created earlier in the same workflow run. The issue should contain clear, actionable requirements. Either issue_number or pull_number must be provided, but not both.",
- "type": [
- "number",
- "string"
- ]
- },
- "pull_number": {
- "description": "Pull request number to assign the Copilot coding agent to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/pull/456). Either issue_number or pull_number must be provided, but not both.",
- "type": [
- "number",
- "string"
- ]
- },
- "pull_request_repo": {
- "description": "Target repository where the pull request should be created, in 'owner/repo' format. If omitted, the PR will be created in the same repository as the issue. This allows issues and code to live in different repositories. The global pull-request-repo configuration (if set) is automatically allowed; additional repositories must be listed in allowed-pull-request-repos.",
- "type": "string"
- }
- },
- "type": "object"
- },
- "name": "assign_to_agent"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
- },
- "data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- }
- },
- "required": [],
- "type": "object"
- },
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
- {
- "add_comment": {
- "defaultMax": 1,
- "fields": {
- "body": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "item_number": {
- "issueOrPRNumber": true
- },
- "repo": {
- "type": "string",
- "maxLength": 256
- }
- }
- },
- "assign_to_agent": {
- "defaultMax": 1,
- "fields": {
- "agent": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- },
- "issue_number": {
- "issueNumberOrTemporaryId": true
- },
- "pull_number": {
- "optionalPositiveInteger": true
- },
- "pull_request_repo": {
- "type": "string",
- "maxLength": 256
- },
- "repo": {
- "type": "string",
- "maxLength": 256
- }
- },
- "customValidation": "requiresOneOf:issue_number,pull_number"
- },
- "close_issue": {
- "defaultMax": 1,
- "fields": {
- "body": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "issue_number": {
- "optionalPositiveInteger": true
- },
- "repo": {
- "type": "string",
- "maxLength": 256
- }
- }
- },
- "create_issue": {
- "defaultMax": 1,
- "fields": {
- "body": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
- },
- "parent": {
- "issueOrPRNumber": true
- },
- "repo": {
- "type": "string",
- "maxLength": 256
- },
- "temporary_id": {
- "type": "string"
- },
- "title": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "missing_data": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "context": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "data_type": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- },
- "reason": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- }
- }
- },
- "missing_tool": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 512
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "tool": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "noop": {
- "defaultMax": 1,
- "fields": {
- "message": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- }
- }
- }
- }
- GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
- - name: Generate Safe Outputs MCP Server Config
- id: safe-outputs-config
- run: |
- # Generate a secure random API key (360 bits of entropy, 40+ chars)
- # Mask immediately to prevent timing vulnerabilities
- API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
- echo "::add-mask::${API_KEY}"
-
- PORT=3001
-
- # Set outputs for next steps
- {
- echo "safe_outputs_api_key=${API_KEY}"
- echo "safe_outputs_port=${PORT}"
- } >> "$GITHUB_OUTPUT"
-
- echo "Safe Outputs MCP server will run on port ${PORT}"
-
- - name: Start Safe Outputs MCP HTTP Server
- id: safe-outputs-start
- env:
- DEBUG: '*'
- GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
- GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- run: |
- # Environment variables are set above to prevent template injection
- export DEBUG
- export GH_AW_SAFE_OUTPUTS_PORT
- export GH_AW_SAFE_OUTPUTS_API_KEY
- export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
- export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
- export GH_AW_MCP_LOG_DIR
-
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
-
- - name: Start MCP Gateway
- id: start-mcp-gateway
- env:
- GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
- GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- run: |
- set -eo pipefail
- mkdir -p /tmp/gh-aw/mcp-config
-
- # Export gateway environment variables for MCP config and gateway script
- export MCP_GATEWAY_PORT="80"
- export MCP_GATEWAY_DOMAIN="host.docker.internal"
- MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
- echo "::add-mask::${MCP_GATEWAY_API_KEY}"
- export MCP_GATEWAY_API_KEY
- export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
- mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
- export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
- export DEBUG="*"
-
- export GH_AW_ENGINE="copilot"
- 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_LOCKDOWN -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.1.6'
-
- mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
- {
- "mcpServers": {
- "github": {
- "type": "stdio",
- "container": "ghcr.io/github/github-mcp-server:v0.31.0",
- "env": {
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
- "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
- "GITHUB_READ_ONLY": "1",
- "GITHUB_TOOLSETS": "context,repos,issues"
- }
- },
- "safeoutputs": {
- "type": "http",
- "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
- "headers": {
- "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
- }
- }
- },
- "gateway": {
- "port": $MCP_GATEWAY_PORT,
- "domain": "${MCP_GATEWAY_DOMAIN}",
- "apiKey": "${MCP_GATEWAY_API_KEY}",
- "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
- }
- }
- GH_AW_MCP_CONFIG_EOF
- - name: Download activation artifact
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
- with:
- name: activation
- path: /tmp/gh-aw
- - name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
- - name: Execute GitHub Copilot CLI
- id: agentic_execution
- # Copilot CLI tool arguments (sorted):
- timeout-minutes: 20
- run: |
- set -o pipefail
- # shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-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,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,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" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \
- -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
- env:
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
- GH_AW_MODEL_AGENT_COPILOT: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }}
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }}
- GITHUB_WORKSPACE: ${{ github.workspace }}
- XDG_CONFIG_HOME: /home/runner
- - name: Configure Git credentials
- env:
- REPO_NAME: ${{ github.repository }}
- SERVER_URL: ${{ github.server_url }}
- run: |
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
- git config --global user.name "github-actions[bot]"
- git config --global am.keepcr true
- # Re-authenticate git with GitHub token
- SERVER_URL_STRIPPED="${SERVER_URL#https://}"
- git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
- echo "Git configured with standard GitHub Actions identity"
- - name: Copy Copilot session state files to logs
- if: always()
- continue-on-error: true
- run: |
- # Copy Copilot session state files to logs folder for artifact collection
- # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them
- SESSION_STATE_DIR="$HOME/.copilot/session-state"
- LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs"
-
- if [ -d "$SESSION_STATE_DIR" ]; then
- echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR"
- mkdir -p "$LOGS_DIR"
- cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true
- echo "Session state files copied successfully"
- else
- echo "No session-state directory found at $SESSION_STATE_DIR"
- fi
- - name: Stop MCP Gateway
- if: always()
- continue-on-error: true
- env:
- MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
- MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
- GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
- run: |
- bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
- - name: Redact secrets in logs
- if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs');
- await main();
- env:
- GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
- SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
- SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
- SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- - name: Upload Safe Outputs
- if: always()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- with:
- name: safe-output
- path: ${{ env.GH_AW_SAFE_OUTPUTS }}
- if-no-files-found: warn
- - name: Ingest agent output
- id: collect_output
- if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- 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,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,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"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs');
- await main();
- - name: Upload sanitized agent output
- if: always() && env.GH_AW_AGENT_OUTPUT
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- with:
- name: agent-output
- path: ${{ env.GH_AW_AGENT_OUTPUT }}
- if-no-files-found: warn
- - name: Upload engine output files
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- with:
- name: agent_outputs
- path: |
- /tmp/gh-aw/sandbox/agent/logs/
- /tmp/gh-aw/redacted-urls.log
- if-no-files-found: ignore
- - name: Parse agent logs for step summary
- if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs');
- await main();
- - name: Parse MCP Gateway logs for step summary
- if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs');
- await main();
- - name: Print firewall logs
- if: always()
- continue-on-error: true
- env:
- AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
- run: |
- # Fix permissions on firewall logs so they can be uploaded as artifacts
- # AWF runs with sudo, creating files owned by root
- sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true
- # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
- if command -v awf &> /dev/null; then
- awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
- else
- echo 'AWF binary not installed, skipping firewall log summary'
- fi
- - name: Upload agent artifacts
- if: always()
- continue-on-error: true
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- with:
- name: agent-artifacts
- path: |
- /tmp/gh-aw/aw-prompts/prompt.txt
- /tmp/gh-aw/mcp-logs/
- /tmp/gh-aw/sandbox/firewall/logs/
- /tmp/gh-aw/agent-stdio.log
- /tmp/gh-aw/agent/
- if-no-files-found: ignore
- # --- Threat Detection (inline) ---
- - name: Check if detection needed
- id: detection_guard
- if: always()
- env:
- OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }}
- HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
- run: |
- if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
- echo "run_detection=true" >> "$GITHUB_OUTPUT"
- echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
- else
- echo "run_detection=false" >> "$GITHUB_OUTPUT"
- echo "Detection skipped: no agent outputs or patches to analyze"
- fi
- - name: Clear MCP configuration for detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- rm -f /tmp/gh-aw/mcp-config/mcp-servers.json
- rm -f /home/runner/.copilot/mcp-config.json
- rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
- - name: Prepare threat detection files
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
- cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
- cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
- for f in /tmp/gh-aw/aw-*.patch; do
- [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- done
- echo "Prepared threat detection files:"
- ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- - name: Setup threat detection
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- WORKFLOW_NAME: "Weekly Upstream Sync Agentic Workflow"
- WORKFLOW_DESCRIPTION: "Weekly upstream sync workflow. Checks for new commits in the official\nCopilot SDK (github/copilot-sdk) and assigns to Copilot to port changes."
- HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs');
- await main();
- - name: Ensure threat-detection directory and log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- run: |
- mkdir -p /tmp/gh-aw/threat-detection
- touch /tmp/gh-aw/threat-detection/detection.log
- - name: Execute GitHub Copilot CLI
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- id: detection_agentic_execution
- # Copilot CLI tool arguments (sorted):
- # --allow-tool shell(cat)
- # --allow-tool shell(grep)
- # --allow-tool shell(head)
- # --allow-tool shell(jq)
- # --allow-tool shell(ls)
- # --allow-tool shell(tail)
- # --allow-tool shell(wc)
- timeout-minutes: 20
- run: |
- set -o pipefail
- # shellcheck disable=SC1003
- sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \
- -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_COPILOT:+ --model "$GH_AW_MODEL_DETECTION_COPILOT"}' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
- env:
- COPILOT_AGENT_RUNNER_TYPE: STANDALONE
- COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- GH_AW_MODEL_DETECTION_COPILOT: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }}
- GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GITHUB_API_URL: ${{ github.api_url }}
- GITHUB_HEAD_REF: ${{ github.head_ref }}
- GITHUB_REF_NAME: ${{ github.ref_name }}
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }}
- GITHUB_WORKSPACE: ${{ github.workspace }}
- XDG_CONFIG_HOME: /home/runner
- - name: Parse threat detection results
- id: parse_detection_results
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- with:
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs');
- await main();
- - name: Upload threat detection log
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- with:
- name: threat-detection.log
- path: /tmp/gh-aw/threat-detection/detection.log
- if-no-files-found: ignore
- - name: Set detection conclusion
- id: detection_conclusion
- if: always()
- env:
- RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
- DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }}
- run: |
- if [[ "$RUN_DETECTION" != "true" ]]; then
- echo "conclusion=skipped" >> "$GITHUB_OUTPUT"
- echo "success=true" >> "$GITHUB_OUTPUT"
- echo "Detection was not needed, marking as skipped"
- elif [[ "$DETECTION_SUCCESS" == "true" ]]; then
- echo "conclusion=success" >> "$GITHUB_OUTPUT"
- echo "success=true" >> "$GITHUB_OUTPUT"
- echo "Detection passed successfully"
- else
- echo "conclusion=failure" >> "$GITHUB_OUTPUT"
- echo "success=false" >> "$GITHUB_OUTPUT"
- echo "Detection found issues"
- fi
-
- conclusion:
- needs:
- - activation
- - agent
- - safe_outputs
- if: (always()) && (needs.agent.result != 'skipped')
- runs-on: ubuntu-slim
- permissions:
- contents: read
- discussions: write
- issues: write
- pull-requests: write
- outputs:
- noop_message: ${{ steps.noop.outputs.noop_message }}
- tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
- total_count: ${{ steps.missing_tool.outputs.total_count }}
- steps:
- - name: Setup Scripts
- uses: github/gh-aw/actions/setup@33cd6c7f1fee588654ef19def2e6a4174be66197 # v0.51.6
- with:
- destination: /opt/gh-aw/actions
- - name: Download agent output artifact
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
- with:
- name: agent-output
- path: /tmp/gh-aw/safeoutputs/
- - name: Setup agent output environment variable
- run: |
- mkdir -p /tmp/gh-aw/safeoutputs/
- find "/tmp/gh-aw/safeoutputs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
- - name: Process No-Op Messages
- id: noop
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_NOOP_MAX: "1"
- GH_AW_WORKFLOW_NAME: "Weekly Upstream Sync Agentic Workflow"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/noop.cjs');
- await main();
- - name: Record Missing Tool
- id: missing_tool
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Weekly Upstream Sync Agentic Workflow"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/missing_tool.cjs');
- await main();
- - name: Handle Agent Failure
- id: handle_agent_failure
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Weekly Upstream Sync Agentic Workflow"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_WORKFLOW_ID: "weekly-upstream-sync"
- GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
- GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
- GH_AW_ASSIGNMENT_ERRORS: ${{ needs.safe_outputs.outputs.assign_to_agent_assignment_errors }}
- GH_AW_ASSIGNMENT_ERROR_COUNT: ${{ needs.safe_outputs.outputs.assign_to_agent_assignment_error_count }}
- GH_AW_GROUP_REPORTS: "false"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs');
- await main();
- - name: Handle No-Op Message
- id: handle_noop_message
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Weekly Upstream Sync Agentic Workflow"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }}
- GH_AW_NOOP_REPORT_AS_ISSUE: "false"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs');
- await main();
-
- safe_outputs:
- needs: agent
- if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true')
- runs-on: ubuntu-slim
- permissions:
- contents: read
- discussions: write
- issues: write
- pull-requests: write
- timeout-minutes: 15
- env:
- GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/${{ github.workflow }}"
- GH_AW_ENGINE_ID: "copilot"
- GH_AW_WORKFLOW_ID: "weekly-upstream-sync"
- GH_AW_WORKFLOW_NAME: "Weekly Upstream Sync Agentic Workflow"
- outputs:
- assign_to_agent_assigned: ${{ steps.assign_to_agent.outputs.assigned }}
- assign_to_agent_assignment_error_count: ${{ steps.assign_to_agent.outputs.assignment_error_count }}
- assign_to_agent_assignment_errors: ${{ steps.assign_to_agent.outputs.assignment_errors }}
- code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
- code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
- comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }}
- comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }}
- create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
- create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
- created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
- process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
- process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
- steps:
- - name: Setup Scripts
- uses: github/gh-aw/actions/setup@33cd6c7f1fee588654ef19def2e6a4174be66197 # v0.51.6
- with:
- destination: /opt/gh-aw/actions
- - name: Download agent output artifact
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
- with:
- name: agent-output
- path: /tmp/gh-aw/safeoutputs/
- - name: Setup agent output environment variable
- run: |
- mkdir -p /tmp/gh-aw/safeoutputs/
- find "/tmp/gh-aw/safeoutputs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
- - name: Process Safe Outputs
- id: process_safe_outputs
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- 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,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,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"
- GITHUB_SERVER_URL: ${{ github.server_url }}
- GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":10,\"target\":\"*\"},\"close_issue\":{\"max\":10,\"required_labels\":[\"upstream-sync\"],\"target\":\"*\"},\"create_issue\":{\"assignees\":[\"copilot\"],\"expires\":144,\"labels\":[\"upstream-sync\"],\"max\":1,\"title_prefix\":\"[upstream-sync] \"},\"missing_data\":{},\"missing_tool\":{}}"
- GH_AW_ASSIGN_COPILOT: "true"
- with:
- github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs');
- await main();
- - name: Assign Copilot to created issues
- if: steps.process_safe_outputs.outputs.issues_to_assign_copilot != ''
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_ISSUES_TO_ASSIGN_COPILOT: ${{ steps.process_safe_outputs.outputs.issues_to_assign_copilot }}
- with:
- github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/assign_copilot_to_created_issues.cjs');
- await main();
- - name: Assign to agent
- id: assign_to_agent
- if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'assign_to_agent'))
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_AGENT_MAX_COUNT: 1
- GH_AW_AGENT_DEFAULT: "copilot"
- GH_AW_AGENT_DEFAULT_MODEL: "claude-opus-4.6"
- GH_AW_AGENT_TARGET: "*"
- GH_AW_TEMPORARY_ID_MAP: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
- with:
- github-token: ${{ secrets.GH_AW_AGENT_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
- const { main } = require('/opt/gh-aw/actions/assign_to_agent.cjs');
- await main();
- - name: Upload safe output items manifest
- if: always()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- with:
- name: safe-output-items
- path: /tmp/safe-output-items.jsonl
- if-no-files-found: warn
-
diff --git a/.github/workflows/weekly-upstream-sync.md b/.github/workflows/weekly-upstream-sync.md
deleted file mode 100644
index 641f29301..000000000
--- a/.github/workflows/weekly-upstream-sync.md
+++ /dev/null
@@ -1,151 +0,0 @@
----
-description: |
- Weekly upstream sync workflow. Checks for new commits in the official
- Copilot SDK (github/copilot-sdk) and assigns to Copilot to port changes.
-
-on:
- schedule: weekly
- workflow_dispatch:
-
-permissions:
- contents: read
- actions: read
- issues: read
-
-network:
- allowed:
- - defaults
- - github
-
-tools:
- github:
- toolsets: [context, repos, issues]
-
-safe-outputs:
- create-issue:
- title-prefix: "[upstream-sync] "
- assignees: [copilot]
- labels: [upstream-sync]
- expires: 6
- close-issue:
- required-labels: [upstream-sync]
- target: "*"
- max: 10
- add-comment:
- target: "*"
- max: 10
- assign-to-agent:
- name: "copilot"
- model: "claude-opus-4.6"
- target: "*"
- noop:
- report-as-issue: false
----
-# Weekly Upstream Sync Agentic Workflow
-This document describes the `weekly-upstream-sync.yml` GitHub Actions workflow, which automates the detection of new changes in the official [Copilot SDK](https://github.com/github/copilot-sdk) and delegates the merge work to the Copilot coding agent.
-
-## Overview
-
-The workflow runs on a **weekly schedule** (every Monday at 10:00 UTC) and can also be triggered manually. It does **not** perform the actual merge — instead, it detects upstream changes and creates a GitHub issue assigned to `copilot`, instructing the agent to follow the [agentic-merge-upstream](../prompts/agentic-merge-upstream.prompt.md) prompt to port the changes.
-
-The agent must also create the Pull Request with the label `upstream-sync`. This allows the workflow to track the merge progress and avoid creating duplicate issues if the agent is still working on a previous sync.
-
-## Trigger
-
-| Trigger | Schedule |
-|---|---|
-| `schedule` | Every Monday at 10:00 UTC (`0 10 * * 1`) |
-| `workflow_dispatch` | Manual trigger from the Actions tab |
-
-## Workflow Steps
-
-### 1. Checkout repository
-
-Checks out the repo to read the `.lastmerge` file, which contains the SHA of the last upstream commit that was merged into the Java SDK.
-
-### 2. Check for upstream changes
-
-- Reads the last merged commit hash from `.lastmerge`
-- Clones the upstream `github/copilot-sdk` repository
-- Compares `.lastmerge` against upstream `HEAD`
-- If they match: sets `has_changes=false`
-- If they differ: counts new commits, generates a summary (up to 20 most recent), and sets outputs (`commit_count`, `upstream_head`, `last_merge`, `summary`)
-
-### 3. Close previous upstream-sync issues (when changes found)
-
-**Condition:** `has_changes == true`
-
-Before creating a new issue, closes any existing open issues with the `upstream-sync` label. This prevents stale issues from accumulating when previous sync attempts were incomplete or superseded. Each closed issue receives a comment explaining it was superseded.
-
-### 4. Close stale upstream-sync issues (when no changes found)
-
-**Condition:** `has_changes == false`
-
-If the upstream is already up to date, closes any lingering open `upstream-sync` issues with a comment noting that no changes were detected. This handles the case where a previous issue was created but the changes were merged manually (updating `.lastmerge`) before the agent completed.
-
-### 5. Create issue and assign to Copilot
-
-**Condition:** `has_changes == true`
-
-Creates a new GitHub issue with:
-
-- **Title:** `Upstream sync: N new commits (YYYY-MM-DD)`
-- **Label:** `upstream-sync`
-- **Assignee:** `copilot`
-- **Body:** Contains commit count, commit range links, a summary of recent commits, and a link to the merge prompt
-
-The Copilot coding agent picks up the issue, creates a branch and PR, then follows the merge prompt to port the changes.
-
-### 6. Summary
-
-Writes a GitHub Actions step summary with:
-
-- Whether changes were detected
-- Commit count and range
-- Recent upstream commits
-- Link to the created issue (if any)
-
-## Flow Diagram
-
-```
-┌─────────────────────┐
-│ Schedule / Manual │
-└──────────┬──────────┘
- │
- ▼
-┌─────────────────────┐
-│ Read .lastmerge │
-│ Clone upstream SDK │
-│ Compare commits │
-└──────────┬──────────┘
- │
- ┌─────┴─────┐
- │ │
- changes? no changes
- │ │
- ▼ ▼
-┌──────────┐ ┌──────────────────┐
-│ Close old│ │ Close stale │
-│ issues │ │ issues │
-└────┬─────┘ └──────────────────┘
- │
- ▼
-┌──────────────────────────┐
-│ Create issue assigned to │
-│ copilot │
-└──────────────────────────┘
- │
- ▼
-┌──────────────────────────┐
-│ Agent follows prompt to │
-│ port changes → PR │
-└──────────────────────────┘
-```
-
-## Related Files
-
-| File | Purpose |
-|---|---|
-| `.lastmerge` | Stores the SHA of the last merged upstream commit |
-| [agentic-merge-upstream.prompt.md](../prompts/agentic-merge-upstream.prompt.md) | Detailed instructions the Copilot agent follows to port changes |
-| `.github/scripts/upstream-sync/` | Helper scripts used by the merge prompt |
diff --git a/.gitignore b/.gitignore
index ddb2508ba..35ea546f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,9 +5,11 @@ examples-test/
blog-copilotsdk/
.claude/worktrees
smoke-test
-*job-logs.txt
+*job-logs.txt*
temporary-prompts/
changebundle.txt*
.classpath
.project
.settings
+scripts/codegen/node_modules/
+*~
diff --git a/.lastmerge b/.lastmerge
index a0cf76b72..83feb636c 100644
--- a/.lastmerge
+++ b/.lastmerge
@@ -1 +1 @@
-40887393a9e687dacc141a645799441b0313ff15
+c3fa6cbfb83d4a20b7912b1a17013d48f5a277a1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c54038b9c..130bc5105 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,28 +8,54 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
-> **Upstream sync:** [`github/copilot-sdk@4088739`](https://github.com/github/copilot-sdk/commit/40887393a9e687dacc141a645799441b0313ff15)
+> **Reference implementation sync:** [`github/copilot-sdk@c3fa6cb`](https://github.com/github/copilot-sdk/commit/c3fa6cbfb83d4a20b7912b1a17013d48f5a277a1)
+## [0.2.2-java.1] - 2026-04-07
+
+> **Reference implementation sync:** [`github/copilot-sdk@c3fa6cb`](https://github.com/github/copilot-sdk/commit/c3fa6cbfb83d4a20b7912b1a17013d48f5a277a1)
+### Added
+
+- Slash commands — register `/command` handlers invoked from the CLI TUI via `SessionConfig.setCommands()` (reference implementation: [`f7fd757`](https://github.com/github/copilot-sdk/commit/f7fd757))
+- `CommandDefinition`, `CommandContext`, `CommandHandler`, `CommandWireDefinition` — types for defining and handling slash commands
+- `CommandExecuteEvent` — event dispatched when a registered slash command is executed
+- Elicitation (UI dialogs) — incoming handler via `SessionConfig.setOnElicitationRequest()` and outgoing convenience methods via `session.getUi()` (reference implementation: [`f7fd757`](https://github.com/github/copilot-sdk/commit/f7fd757))
+- `ElicitationContext`, `ElicitationHandler`, `ElicitationParams`, `ElicitationResult`, `ElicitationResultAction`, `ElicitationSchema`, `InputOptions` — types for elicitation
+- `ElicitationRequestedEvent` — event dispatched when an elicitation request is received
+- `SessionUiApi` — convenience API on `session.getUi()` for `confirm()`, `select()`, `input()`, and `elicitation()` calls
+- `SessionCapabilities` and `SessionUiCapabilities` — session capability reporting populated from create/resume response
+- `CapabilitiesChangedEvent` — event dispatched when session capabilities are updated
+- `CopilotClient.getSessionMetadata(String)` — O(1) session lookup by ID
+- `GetSessionMetadataResponse` — response type for `getSessionMetadata`
+
+### Fixed
+
+- Permission events already resolved by a pre-hook now short-circuit before invoking the client-side handler
+- `SessionUiApi` Javadoc now uses valid Java null-check syntax instead of `?.`
+- README updated to say "GitHub Copilot CLI 1.0.17" instead of "GitHub Copilot 1.0.17"
+
+## [0.2.1-java.1] - 2026-04-02
+
+> **Reference implementation sync:** [`github/copilot-sdk@4088739`](https://github.com/github/copilot-sdk/commit/40887393a9e687dacc141a645799441b0313ff15)
## [0.2.1-java.0] - 2026-03-26
-> **Upstream sync:** [`github/copilot-sdk@4088739`](https://github.com/github/copilot-sdk/commit/40887393a9e687dacc141a645799441b0313ff15)
+> **Reference implementation sync:** [`github/copilot-sdk@4088739`](https://github.com/github/copilot-sdk/commit/40887393a9e687dacc141a645799441b0313ff15)
### Added
-- `UnknownSessionEvent` — forward-compatible placeholder for event types not yet known to the SDK; unknown events are now dispatched to handlers instead of being silently dropped (upstream: [`d82fd62`](https://github.com/github/copilot-sdk/commit/d82fd62))
-- `PermissionRequestResultKind.NO_RESULT` — new constant that signals the handler intentionally abstains from answering a permission request, leaving it unanswered for another client (upstream: [`df59a0e`](https://github.com/github/copilot-sdk/commit/df59a0e))
-- `ToolDefinition.skipPermission` field and `ToolDefinition.createSkipPermission()` factory — marks a tool to skip the permission prompt (upstream: [`10c4d02`](https://github.com/github/copilot-sdk/commit/10c4d02))
-- `SystemMessageMode.CUSTOMIZE` — new enum value for fine-grained system prompt customization (upstream: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
-- `SectionOverrideAction` enum — specifies the operation on a system prompt section (replace, remove, append, prepend, transform) (upstream: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
-- `SectionOverride` class — describes how one section of the system prompt should be modified, with optional transform callback (upstream: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
-- `SystemPromptSections` constants — well-known section identifier strings for use with CUSTOMIZE mode (upstream: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
-- `SystemMessageConfig.setSections(Map)` — section-level overrides for CUSTOMIZE mode (upstream: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
-- `systemMessage.transform` RPC handler — the SDK now registers a handler that invokes transform callbacks registered in the session config (upstream: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
-- `CopilotSession.setModel(String, String)` — new overload that accepts an optional reasoning effort level (upstream: [`ea90f07`](https://github.com/github/copilot-sdk/commit/ea90f07))
+- `UnknownSessionEvent` — forward-compatible placeholder for event types not yet known to the SDK; unknown events are now dispatched to handlers instead of being silently dropped (reference implementation: [`d82fd62`](https://github.com/github/copilot-sdk/commit/d82fd62))
+- `PermissionRequestResultKind.NO_RESULT` — new constant that signals the handler intentionally abstains from answering a permission request, leaving it unanswered for another client (reference implementation: [`df59a0e`](https://github.com/github/copilot-sdk/commit/df59a0e))
+- `ToolDefinition.skipPermission` field and `ToolDefinition.createSkipPermission()` factory — marks a tool to skip the permission prompt (reference implementation: [`10c4d02`](https://github.com/github/copilot-sdk/commit/10c4d02))
+- `SystemMessageMode.CUSTOMIZE` — new enum value for fine-grained system prompt customization (reference implementation: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
+- `SectionOverrideAction` enum — specifies the operation on a system prompt section (replace, remove, append, prepend, transform) (reference implementation: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
+- `SectionOverride` class — describes how one section of the system prompt should be modified, with optional transform callback (reference implementation: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
+- `SystemPromptSections` constants — well-known section identifier strings for use with CUSTOMIZE mode (reference implementation: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
+- `SystemMessageConfig.setSections(Map)` — section-level overrides for CUSTOMIZE mode (reference implementation: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
+- `systemMessage.transform` RPC handler — the SDK now registers a handler that invokes transform callbacks registered in the session config (reference implementation: [`005b780`](https://github.com/github/copilot-sdk/commit/005b780))
+- `CopilotSession.setModel(String, String)` — new overload that accepts an optional reasoning effort level (reference implementation: [`ea90f07`](https://github.com/github/copilot-sdk/commit/ea90f07))
- `CopilotSession.log(String, String, Boolean, String)` — new overload with an optional `url` parameter (minor addition)
-- `BlobAttachment` class — inline base64-encoded binary attachment for messages (e.g., images) (upstream: [`698b259`](https://github.com/github/copilot-sdk/commit/698b259))
+- `BlobAttachment` class — inline base64-encoded binary attachment for messages (e.g., images) (reference implementation: [`698b259`](https://github.com/github/copilot-sdk/commit/698b259))
- `MessageAttachment` sealed interface — type-safe base for all attachment types (`Attachment`, `BlobAttachment`), with Jackson polymorphic serialization support
-- `TelemetryConfig` class — OpenTelemetry configuration for the CLI server; set on `CopilotClientOptions.setTelemetry()` (upstream: [`f2d21a0`](https://github.com/github/copilot-sdk/commit/f2d21a0))
-- `CopilotClientOptions.setTelemetry(TelemetryConfig)` — enables OpenTelemetry instrumentation in the CLI server (upstream: [`f2d21a0`](https://github.com/github/copilot-sdk/commit/f2d21a0))
+- `TelemetryConfig` class — OpenTelemetry configuration for the CLI server; set on `CopilotClientOptions.setTelemetry()` (reference implementation: [`f2d21a0`](https://github.com/github/copilot-sdk/commit/f2d21a0))
+- `CopilotClientOptions.setTelemetry(TelemetryConfig)` — enables OpenTelemetry instrumentation in the CLI server (reference implementation: [`f2d21a0`](https://github.com/github/copilot-sdk/commit/f2d21a0))
### Changed
@@ -44,61 +70,61 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [0.1.32-java.0] - 2026-03-17
-> **Upstream sync:** [`github/copilot-sdk@062b61c`](https://github.com/github/copilot-sdk/commit/062b61c8aa63b9b5d45fa1d7b01723e6660ffa83)
+> **Reference implementation sync:** [`github/copilot-sdk@062b61c`](https://github.com/github/copilot-sdk/commit/062b61c8aa63b9b5d45fa1d7b01723e6660ffa83)
## [1.0.11] - 2026-03-12
-> **Upstream sync:** [`github/copilot-sdk@062b61c`](https://github.com/github/copilot-sdk/commit/062b61c8aa63b9b5d45fa1d7b01723e6660ffa83)
+> **Reference implementation sync:** [`github/copilot-sdk@062b61c`](https://github.com/github/copilot-sdk/commit/062b61c8aa63b9b5d45fa1d7b01723e6660ffa83)
### Added
-- `CopilotClientOptions.setOnListModels(Supplier>>)` — custom handler for `listModels()` used in BYOK mode to return models from a custom provider instead of querying the CLI (upstream: [`e478657`](https://github.com/github/copilot-sdk/commit/e478657))
-- `SessionConfig.setAgent(String)` — pre-selects a custom agent by name when creating a session (upstream: [`7766b1a`](https://github.com/github/copilot-sdk/commit/7766b1a))
-- `ResumeSessionConfig.setAgent(String)` — pre-selects a custom agent by name when resuming a session (upstream: [`7766b1a`](https://github.com/github/copilot-sdk/commit/7766b1a))
-- `SessionConfig.setOnEvent(Consumer)` — registers an event handler before the `session.create` RPC is issued, ensuring no early events are missed (upstream: [`4125fe7`](https://github.com/github/copilot-sdk/commit/4125fe7))
-- `ResumeSessionConfig.setOnEvent(Consumer)` — registers an event handler before the `session.resume` RPC is issued (upstream: [`4125fe7`](https://github.com/github/copilot-sdk/commit/4125fe7))
-- New broadcast session event types (protocol v3): `ExternalToolRequestedEvent` (`external_tool.requested`), `ExternalToolCompletedEvent` (`external_tool.completed`), `PermissionRequestedEvent` (`permission.requested`), `PermissionCompletedEvent` (`permission.completed`), `CommandQueuedEvent` (`command.queued`), `CommandCompletedEvent` (`command.completed`), `ExitPlanModeRequestedEvent` (`exit_plan_mode.requested`), `ExitPlanModeCompletedEvent` (`exit_plan_mode.completed`), `SystemNotificationEvent` (`system.notification`) (upstream: [`1653812`](https://github.com/github/copilot-sdk/commit/1653812), [`396e8b3`](https://github.com/github/copilot-sdk/commit/396e8b3))
-- `CopilotSession.log(String)` and `CopilotSession.log(String, String, Boolean)` — log a message to the session timeline (upstream: [`4125fe7`](https://github.com/github/copilot-sdk/commit/4125fe7))
+- `CopilotClientOptions.setOnListModels(Supplier>>)` — custom handler for `listModels()` used in BYOK mode to return models from a custom provider instead of querying the CLI (reference implementation: [`e478657`](https://github.com/github/copilot-sdk/commit/e478657))
+- `SessionConfig.setAgent(String)` — pre-selects a custom agent by name when creating a session (reference implementation: [`7766b1a`](https://github.com/github/copilot-sdk/commit/7766b1a))
+- `ResumeSessionConfig.setAgent(String)` — pre-selects a custom agent by name when resuming a session (reference implementation: [`7766b1a`](https://github.com/github/copilot-sdk/commit/7766b1a))
+- `SessionConfig.setOnEvent(Consumer)` — registers an event handler before the `session.create` RPC is issued, ensuring no early events are missed (reference implementation: [`4125fe7`](https://github.com/github/copilot-sdk/commit/4125fe7))
+- `ResumeSessionConfig.setOnEvent(Consumer)` — registers an event handler before the `session.resume` RPC is issued (reference implementation: [`4125fe7`](https://github.com/github/copilot-sdk/commit/4125fe7))
+- New broadcast session event types (protocol v3): `ExternalToolRequestedEvent` (`external_tool.requested`), `ExternalToolCompletedEvent` (`external_tool.completed`), `PermissionRequestedEvent` (`permission.requested`), `PermissionCompletedEvent` (`permission.completed`), `CommandQueuedEvent` (`command.queued`), `CommandCompletedEvent` (`command.completed`), `ExitPlanModeRequestedEvent` (`exit_plan_mode.requested`), `ExitPlanModeCompletedEvent` (`exit_plan_mode.completed`), `SystemNotificationEvent` (`system.notification`) (reference implementation: [`1653812`](https://github.com/github/copilot-sdk/commit/1653812), [`396e8b3`](https://github.com/github/copilot-sdk/commit/396e8b3))
+- `CopilotSession.log(String)` and `CopilotSession.log(String, String, Boolean)` — log a message to the session timeline (reference implementation: [`4125fe7`](https://github.com/github/copilot-sdk/commit/4125fe7))
### Changed
-- **Protocol version bumped to v3.** The SDK now supports CLI servers running v2 or v3 (backward-compatible range). Sessions are now registered in the client's session map *before* the `session.create`/`session.resume` RPC is issued, ensuring broadcast events emitted immediately on session start are never dropped (upstream: [`4125fe7`](https://github.com/github/copilot-sdk/commit/4125fe7), [`1653812`](https://github.com/github/copilot-sdk/commit/1653812))
-- In protocol v3, tool calls and permission requests that have a registered handler are now handled automatically via `ExternalToolRequestedEvent` and `PermissionRequestedEvent` broadcast events; results are sent back via `session.tools.handlePendingToolCall` and `session.permissions.handlePendingPermissionRequest` RPC calls (upstream: [`1653812`](https://github.com/github/copilot-sdk/commit/1653812))
+- **Protocol version bumped to v3.** The SDK now supports CLI servers running v2 or v3 (backward-compatible range). Sessions are now registered in the client's session map *before* the `session.create`/`session.resume` RPC is issued, ensuring broadcast events emitted immediately on session start are never dropped (reference implementation: [`4125fe7`](https://github.com/github/copilot-sdk/commit/4125fe7), [`1653812`](https://github.com/github/copilot-sdk/commit/1653812))
+- In protocol v3, tool calls and permission requests that have a registered handler are now handled automatically via `ExternalToolRequestedEvent` and `PermissionRequestedEvent` broadcast events; results are sent back via `session.tools.handlePendingToolCall` and `session.permissions.handlePendingPermissionRequest` RPC calls (reference implementation: [`1653812`](https://github.com/github/copilot-sdk/commit/1653812))
## [1.0.10] - 2026-03-03
-> **Upstream sync:** [`github/copilot-sdk@dcd86c1`](https://github.com/github/copilot-sdk/commit/dcd86c189501ce1b46b787ca60d90f3f315f3079)
+> **Reference implementation sync:** [`github/copilot-sdk@dcd86c1`](https://github.com/github/copilot-sdk/commit/dcd86c189501ce1b46b787ca60d90f3f315f3079)
### Added
-- `CopilotSession.setModel(String)` — changes the model for an existing session mid-conversation; the new model takes effect for the next message, and conversation history is preserved (upstream: [`bd98e3a`](https://github.com/github/copilot-sdk/commit/bd98e3a))
-- `ToolDefinition.createOverride(String, String, Map, ToolHandler)` — creates a tool definition that overrides a built-in CLI tool with the same name (upstream: [`f843c80`](https://github.com/github/copilot-sdk/commit/f843c80))
-- `ToolDefinition` record now includes `overridesBuiltInTool` field; when `true`, signals to the CLI that the custom tool intentionally replaces a built-in (upstream: [`f843c80`](https://github.com/github/copilot-sdk/commit/f843c80))
-- `CopilotSession.listAgents()` — lists custom agents available for selection (upstream: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
-- `CopilotSession.getCurrentAgent()` — gets the currently selected custom agent (upstream: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
-- `CopilotSession.selectAgent(String)` — selects a custom agent for the session (upstream: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
-- `CopilotSession.deselectAgent()` — deselects the current custom agent (upstream: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
-- `CopilotSession.compact()` — triggers immediate session context compaction (upstream: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
-- `AgentInfo` — new JSON type representing a custom agent with `name`, `displayName`, and `description` (upstream: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
-- New event types: `SessionTaskCompleteEvent` (`session.task_complete`), `AssistantStreamingDeltaEvent` (`assistant.streaming_delta`), `SubagentDeselectedEvent` (`subagent.deselected`) (upstream: various commits)
+- `CopilotSession.setModel(String)` — changes the model for an existing session mid-conversation; the new model takes effect for the next message, and conversation history is preserved (reference implementation: [`bd98e3a`](https://github.com/github/copilot-sdk/commit/bd98e3a))
+- `ToolDefinition.createOverride(String, String, Map, ToolHandler)` — creates a tool definition that overrides a built-in CLI tool with the same name (reference implementation: [`f843c80`](https://github.com/github/copilot-sdk/commit/f843c80))
+- `ToolDefinition` record now includes `overridesBuiltInTool` field; when `true`, signals to the CLI that the custom tool intentionally replaces a built-in (reference implementation: [`f843c80`](https://github.com/github/copilot-sdk/commit/f843c80))
+- `CopilotSession.listAgents()` — lists custom agents available for selection (reference implementation: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
+- `CopilotSession.getCurrentAgent()` — gets the currently selected custom agent (reference implementation: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
+- `CopilotSession.selectAgent(String)` — selects a custom agent for the session (reference implementation: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
+- `CopilotSession.deselectAgent()` — deselects the current custom agent (reference implementation: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
+- `CopilotSession.compact()` — triggers immediate session context compaction (reference implementation: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
+- `AgentInfo` — new JSON type representing a custom agent with `name`, `displayName`, and `description` (reference implementation: [`9d998fb`](https://github.com/github/copilot-sdk/commit/9d998fb))
+- New event types: `SessionTaskCompleteEvent` (`session.task_complete`), `AssistantStreamingDeltaEvent` (`assistant.streaming_delta`), `SubagentDeselectedEvent` (`subagent.deselected`) (reference implementation: various commits)
- `AssistantTurnStartEvent` data now includes `interactionId` field
- `AssistantMessageEvent` data now includes `interactionId` field
- `ToolExecutionCompleteEvent` data now includes `model` and `interactionId` fields
- `SkillInvokedEvent` data now includes `pluginName` and `pluginVersion` fields
- `AssistantUsageEvent` data now includes `copilotUsage` field with `CopilotUsage` and `TokenDetails` nested types
-- E2E tests for custom tool permission approval and denial flows (upstream: [`388f2f3`](https://github.com/github/copilot-sdk/commit/388f2f3))
+- E2E tests for custom tool permission approval and denial flows (reference implementation: [`388f2f3`](https://github.com/github/copilot-sdk/commit/388f2f3))
### Changed
-- **Breaking:** `createSession(SessionConfig)` now requires a non-null `onPermissionRequest` handler; throws `IllegalArgumentException` if not provided (upstream: [`279f6c4`](https://github.com/github/copilot-sdk/commit/279f6c4))
-- **Breaking:** `resumeSession(String, ResumeSessionConfig)` now requires a non-null `onPermissionRequest` handler; throws `IllegalArgumentException` if not provided (upstream: [`279f6c4`](https://github.com/github/copilot-sdk/commit/279f6c4))
-- **Breaking:** The no-arg `createSession()` and `resumeSession(String)` overloads were removed (upstream: [`279f6c4`](https://github.com/github/copilot-sdk/commit/279f6c4))
-- `AssistantMessageDeltaEvent` data: `totalResponseSizeBytes` field moved to new `AssistantStreamingDeltaEvent` (upstream: various)
+- **Breaking:** `createSession(SessionConfig)` now requires a non-null `onPermissionRequest` handler; throws `IllegalArgumentException` if not provided (reference implementation: [`279f6c4`](https://github.com/github/copilot-sdk/commit/279f6c4))
+- **Breaking:** `resumeSession(String, ResumeSessionConfig)` now requires a non-null `onPermissionRequest` handler; throws `IllegalArgumentException` if not provided (reference implementation: [`279f6c4`](https://github.com/github/copilot-sdk/commit/279f6c4))
+- **Breaking:** The no-arg `createSession()` and `resumeSession(String)` overloads were removed (reference implementation: [`279f6c4`](https://github.com/github/copilot-sdk/commit/279f6c4))
+- `AssistantMessageDeltaEvent` data: `totalResponseSizeBytes` field moved to new `AssistantStreamingDeltaEvent` (reference implementation: various)
### Fixed
-- Permission checks now also apply to SDK-registered custom tools, invoking the `onPermissionRequest` handler with `kind="custom-tool"` before executing tools (upstream: [`388f2f3`](https://github.com/github/copilot-sdk/commit/388f2f3))
+- Permission checks now also apply to SDK-registered custom tools, invoking the `onPermissionRequest` handler with `kind="custom-tool"` before executing tools (reference implementation: [`388f2f3`](https://github.com/github/copilot-sdk/commit/388f2f3))
## [1.0.9] - 2026-02-16
-> **Upstream sync:** [`github/copilot-sdk@e40d57c`](https://github.com/github/copilot-sdk/commit/e40d57c86e18b495722adbf42045288c03924342)
+> **Reference implementation sync:** [`github/copilot-sdk@e40d57c`](https://github.com/github/copilot-sdk/commit/e40d57c86e18b495722adbf42045288c03924342)
### Added
#### Cookbook with Practical Recipes
@@ -165,7 +191,7 @@ session.on(SessionContextChangedEvent.class, event -> {
## [1.0.8] - 2026-02-08
-> **Upstream sync:** [`github/copilot-sdk@05e3c46`](https://github.com/github/copilot-sdk/commit/05e3c46c8c23130c9c064dc43d00ec78f7a75eab)
+> **Reference implementation sync:** [`github/copilot-sdk@05e3c46`](https://github.com/github/copilot-sdk/commit/05e3c46c8c23130c9c064dc43d00ec78f7a75eab)
### Added
@@ -321,7 +347,7 @@ New types: `GetForegroundSessionResponse`, `SetForegroundSessionResponse`
### Changed
- Enhanced permission request handling with graceful error recovery
-- Updated test harness integration to clone from upstream SDK
+- Updated test harness integration to clone from reference implementation SDK
- Improved logging for session events and user input requests
### Fixed
@@ -340,8 +366,8 @@ New types: `GetForegroundSessionResponse`, `SetForegroundSessionResponse`
### Changed
-- Merged upstream SDK changes (commit 87ff5510)
-- Added agentic-merge-upstream Claude skill for tracking upstream changes
+- Merged reference implementation SDK changes (commit 87ff5510)
+- Added agentic-merge-reference-impl Claude skill for tracking reference implementation changes
### Fixed
@@ -350,7 +376,7 @@ New types: `GetForegroundSessionResponse`, `SetForegroundSessionResponse`
- NPM dependency installation in CI workflow
- Enhanced error handling in permission request processing
- Checkstyle and Maven Resources Plugin version updates
-- Test harness CLI installation to match upstream version
+- Test harness CLI installation to match reference implementation version
## [1.0.4] - 2026-01-27
@@ -368,7 +394,7 @@ New types: `GetForegroundSessionResponse`, `SetForegroundSessionResponse`
- Refactored tool argument handling for improved type safety
- Optimized event listener registration in examples
- Enhanced site navigation with documentation links
-- Merged upstream SDK changes from commit f902b76
+- Merged reference implementation SDK changes from commit f902b76
### Fixed
@@ -384,7 +410,7 @@ New types: `GetForegroundSessionResponse`, `SetForegroundSessionResponse`
- MCP Servers documentation and integration examples
- Infinite sessions documentation section
- Versioned documentation template with version selector
-- Guidelines for porting upstream SDK changes to Java
+- Guidelines for porting reference implementation SDK changes to Java
- Configuration for automatically generated release notes
### Changed
@@ -462,12 +488,23 @@ New types: `GetForegroundSessionResponse`, `SetForegroundSessionResponse`
- Pre-commit hook for Spotless code formatting
- Comprehensive API documentation
-[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.2.1-java.0...HEAD
+[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...HEAD
+[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.2.2-java.1...HEAD
+[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.2.2-java.1...HEAD
+[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.2.1-java.0...HEAD
+[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...HEAD
+[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.2.1-java.0...HEAD
+[Unreleased]: https://github.com/github/copilot-sdk-java/compare/v0.2.2-java.1...HEAD
+[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
[1.0.11]: https://github.com/github/copilot-sdk-java/compare/v1.0.10...v1.0.11
@@ -482,3 +519,4 @@ 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/CONTRIBUTING.md b/CONTRIBUTING.md
index b9320ee1a..f86976c01 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -5,7 +5,7 @@
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
-This repository contains the **GitHub Copilot SDK for Java**, the official Java variant of the official [GitHub Copilot SDK](https://github.com/github/copilot-sdk). For issues or features related to the upstream SDK, please contribute there instead.
+This repository contains the **GitHub Copilot SDK for Java**, the official Java variant of the official [GitHub Copilot SDK](https://github.com/github/copilot-sdk). For issues or features related to the reference implementation SDK, please contribute there instead.
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).
@@ -19,7 +19,7 @@ We'd love your help with:
* Making the SDK more idiomatic and nice to use for Java developers
* Improving documentation
-If you have ideas for entirely new features, please post an issue or start a discussion. We're very open to new features but need to make sure they align with the direction of the upstream [Copilot SDK](https://github.com/github/copilot-sdk) and can be maintained in sync. Note that this repo periodically merges upstream changes — see the [README](README.md#agentic-upstream-merge-and-sync) for details on how that works.
+If you have ideas for entirely new features, please post an issue or start a discussion. We're very open to new features but need to make sure they align with the direction of the [Copilot SDK](https://github.com/github/copilot-sdk) reference implementation and can be maintained in sync. Note that this repo periodically merges reference implementation changes — see the [README](README.md#agentic-reference-implementation-merge-and-sync) for details on how that works.
## Prerequisites for running and testing code
@@ -65,3 +65,4 @@ Here are a few things you can do that will increase the likelihood of your pull
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [GitHub Help](https://help.github.com)
+
diff --git a/README.md b/README.md
index 84f0d6d81..f20bc405a 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
## Background
-> ⚠️ **Disclaimer:** This SDK tracks the pre-GA [GitHub Copilot SDKs](https://github.com/github/copilot-sdk) for [.NET](https://github.com/github/copilot-sdk/tree/main/dotnet) and [nodejs](https://github.com/github/copilot-sdk/tree/main/nodejs). This SDK may change in breaking ways. Use at your own risk.
+> ℹ️ **Public Preview:** This SDK tracks the [GitHub Copilot SDKs](https://github.com/github/copilot-sdk) for [.NET](https://github.com/github/copilot-sdk/tree/main/dotnet) and [Node.js](https://github.com/github/copilot-sdk/tree/main/nodejs). While in public preview, minor breaking changes may still occur between releases.
Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build AI-powered applications and agentic workflows.
@@ -24,8 +24,8 @@ Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build A
### Requirements
-- Java 17 or later
-- GitHub Copilot CLI 0.0.411-1 or later installed and in PATH (or provide custom `cliPath`)
+- Java 17 or later. **JDK 25 recommended**. Selecting JDK 25 enables the use of virtual threads, as shown in the [Quick Start](#quick-start).
+- GitHub Copilot CLI 1.0.17 or later installed and in `PATH` (or provide custom `cliPath`)
### Maven
@@ -33,7 +33,7 @@ Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build A
com.githubcopilot-sdk-java
- 0.2.1-java.0
+ 0.2.2-java.1
```
@@ -53,22 +53,22 @@ Snapshot builds of the next development version are published to Maven Central S
com.githubcopilot-sdk-java
- 0.2.1-java.0-SNAPSHOT
+ 0.2.3-java.1-SNAPSHOT
```
### Gradle
```groovy
-implementation 'com.github:copilot-sdk-java:0.2.1-java.0'
+implementation 'com.github:copilot-sdk-java:0.2.2-java.1'
```
## Quick Start
```java
import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.AssistantMessageEvent;
-import com.github.copilot.sdk.events.SessionUsageInfoEvent;
+import com.github.copilot.sdk.generated.AssistantMessageEvent;
+import com.github.copilot.sdk.generated.SessionUsageInfoEvent;
import com.github.copilot.sdk.json.CopilotClientOptions;
import com.github.copilot.sdk.json.MessageOptions;
import com.github.copilot.sdk.json.PermissionHandler;
@@ -103,9 +103,9 @@ public class CopilotSDK {
session.on(SessionUsageInfoEvent.class, usage -> {
var data = usage.getData();
System.out.println("\n--- Usage Metrics ---");
- System.out.println("Current tokens: " + (int) data.currentTokens());
- System.out.println("Token limit: " + (int) data.tokenLimit());
- System.out.println("Messages count: " + (int) data.messagesLength());
+ System.out.println("Current tokens: " + data.currentTokens().intValue());
+ System.out.println("Token limit: " + data.tokenLimit().intValue());
+ System.out.println("Messages count: " + data.messagesLength().intValue());
});
// Send a message
@@ -153,7 +153,7 @@ jbang https://github.com/github/copilot-sdk-java/blob/latest/jbang-example.java
## CI/CD Workflows
-This project uses several GitHub Actions workflows for building, testing, releasing, and syncing with the upstream SDK.
+This project uses several GitHub Actions workflows for building, testing, releasing, and syncing with the reference implementation SDK.
See [WORKFLOWS.md](docs/WORKFLOWS.md) for a full overview and details on each workflow.
@@ -161,14 +161,14 @@ See [WORKFLOWS.md](docs/WORKFLOWS.md) for a full overview and details on each wo
Contributions are welcome! Please see the [Contributing Guide](CONTRIBUTING.md) for details.
-### Agentic Upstream Merge and Sync
+### Agentic Reference Implementation Merge and Sync
-This SDK tracks the official [Copilot SDK](https://github.com/github/copilot-sdk) (.NET reference implementation) and ports changes to Java. The upstream merge process is automated with AI assistance:
+This SDK tracks the official [Copilot SDK](https://github.com/github/copilot-sdk) (.NET reference implementation) and ports changes to Java. The reference implementation merge process is automated with AI assistance:
-**Weekly automated sync** — A [scheduled GitHub Actions workflow](.github/workflows/weekly-upstream-sync.yml) runs every Monday at 5 AM ET. It checks for new upstream commits since the last merge (tracked in [`.lastmerge`](.lastmerge)), and if changes are found, creates an issue labeled `upstream-sync` and assigns it to the GitHub Copilot coding agent. Any previously open `upstream-sync` issues are automatically closed.
+**Weekly automated sync** — A [scheduled GitHub Actions workflow](.github/workflows/weekly-reference-impl-sync.yml) runs every Monday at 5 AM ET. It checks for new reference implementation commits since the last merge (tracked in [`.lastmerge`](.lastmerge)), and if changes are found, creates an issue labeled `reference-impl-sync` and assigns it to the GitHub Copilot coding agent. Any previously open `reference-impl-sync` issues are automatically closed.
-**Reusable prompt** — The merge workflow is defined in [`agentic-merge-upstream.prompt.md`](.github/prompts/agentic-merge-upstream.prompt.md). It can be triggered manually from:
-- **VS Code Copilot Chat** — type `/agentic-merge-upstream`
+**Reusable prompt** — The merge workflow is defined in [`agentic-merge-reference-impl.prompt.md`](.github/prompts/agentic-merge-reference-impl.prompt.md). It can be triggered manually from:
+- **VS Code Copilot Chat** — type `/agentic-merge-reference-impl`
- **GitHub Copilot CLI** — use `copilot` CLI with the same skill reference
### Development Setup
@@ -212,3 +212,4 @@ MIT — see [LICENSE](LICENSE) for details.
[](https://www.star-history.com/#github/copilot-sdk-java&Date)
⭐ Drop a star if you find this useful!
+
diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml
index 33c9ac5f2..9a6f3761b 100644
--- a/config/checkstyle/checkstyle.xml
+++ b/config/checkstyle/checkstyle.xml
@@ -11,9 +11,9 @@
-
+
-
+
diff --git a/config/spotbugs/spotbugs-exclude.xml b/config/spotbugs/spotbugs-exclude.xml
index 7f3b068cb..1c7d415f6 100644
--- a/config/spotbugs/spotbugs-exclude.xml
+++ b/config/spotbugs/spotbugs-exclude.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/docs/WORKFLOWS.md b/docs/WORKFLOWS.md
index 74afbd337..3df254890 100644
--- a/docs/WORKFLOWS.md
+++ b/docs/WORKFLOWS.md
@@ -5,10 +5,12 @@
| Workflow | Description | Triggers | Schedule |
|----------|-------------|----------|----------|
| [Build & Test](workflows/build-test.yml) | Builds, lints, and tests the Java SDK | `push` (main), `pull_request`, `merge_group`, `workflow_dispatch` | Sundays at 00:00 UTC |
+| [Codegen Check](workflows/codegen-check.yml) | Verifies that generated Java files are up-to-date with the JSON schemas | `push` (main), `pull_request`, `workflow_dispatch` | — |
+| [Update @github/copilot Dependency](workflows/update-copilot-dependency.yml) | Updates the `@github/copilot` npm package, re-runs code generation, and opens a PR | `workflow_dispatch` | — |
| [Deploy Documentation](workflows/deploy-site.yml) | Generates and deploys versioned docs to GitHub Pages | `workflow_run` (after Build & Test), `release`, `workflow_dispatch` | — |
| [Publish to Maven Central](workflows/publish-maven.yml) | Releases the SDK to Maven Central and creates a GitHub Release | `workflow_dispatch` | — |
-| [Weekly Upstream Sync](workflows/weekly-upstream-sync.yml) | Checks for new upstream commits and creates an issue for Copilot to merge | `workflow_dispatch` | Mondays at 10:00 UTC |
-| [Weekly Upstream Sync (Agentic)](workflows/weekly-upstream-sync.lock.yml) | Compiled agentic workflow that executes the upstream sync via `gh-aw` | `workflow_dispatch` | Tuesdays at 08:39 UTC (scattered) |
+| [Weekly Reference Implementation Sync](workflows/weekly-reference-impl-sync.yml) | Checks for new reference implementation commits and creates an issue for Copilot to merge | `workflow_dispatch` | Mondays at 10:00 UTC |
+| [Weekly Reference Implementation Sync (Agentic)](workflows/weekly-reference-impl-sync.lock.yml) | Compiled agentic workflow that executes the reference implementation sync via `gh-aw` | `workflow_dispatch` | Tuesdays at 08:39 UTC (scattered) |
| [Copilot Setup Steps](workflows/copilot-setup-steps.yml) | Configures the environment for the GitHub Copilot coding agent | `push` (on self-change), `workflow_dispatch` | — |
---
@@ -21,9 +23,9 @@ Runs on every push to `main`, on pull requests, and in merge groups. Also runs w
Steps:
1. Checks code formatting with Spotless
-2. Compiles the SDK and clones the upstream test harness
+2. Compiles the SDK and clones the reference implementation test harness
3. Verifies Javadoc generation
-4. Installs the Copilot CLI from the cloned upstream SDK
+4. Installs the Copilot CLI from the cloned reference implementation SDK
5. Runs the full test suite with `mvn verify`
6. Uploads test results (JaCoCo + Surefire) as artifacts for site generation
@@ -53,7 +55,7 @@ Manual-only workflow that performs a full release:
1. Determines release and next development versions (auto-derived from `pom.xml` or manually specified)
2. Updates `CHANGELOG.md`, `README.md`, and `jbang-example.java` with the release version
-3. Injects the upstream sync commit hash from `.lastmerge` into the changelog
+3. Injects the reference implementation sync commit hash from `.lastmerge` into the changelog
4. Runs `mvn release:prepare` and `mvn release:perform` to deploy to Maven Central
5. Creates a GitHub Release with auto-generated notes
6. Moves the `latest` git tag forward
@@ -62,31 +64,67 @@ Manual-only workflow that performs a full release:
---
-## Weekly Upstream Sync
+## Weekly Reference Implementation Sync
-**File:** [`weekly-upstream-sync.yml`](workflows/weekly-upstream-sync.yml)
+**File:** [`weekly-reference-impl-sync.yml`](workflows/weekly-reference-impl-sync.yml)
Runs every Monday at 10:00 UTC. Clones the official `github/copilot-sdk` repository and compares `HEAD` against the commit hash stored in `.lastmerge`.
If new commits are found:
-1. Closes any previously open `upstream-sync` issues
-2. Creates a new issue with a summary of upstream changes
+1. Closes any previously open `reference-impl-sync` issues
+2. Creates a new issue with a summary of reference implementation changes
3. Assigns the issue to `copilot-swe-agent` for automated porting
-If no changes are found, any stale open `upstream-sync` issues are closed.
+If no changes are found, any stale open `reference-impl-sync` issues are closed.
---
-## Weekly Upstream Sync (Agentic Workflow: Experimental)
+## Weekly Reference Implementation Sync (Agentic Workflow: Experimental)
-**File:** [`weekly-upstream-sync.lock.yml`](workflows/weekly-upstream-sync.lock.yml)
+**File:** [`weekly-reference-impl-sync.lock.yml`](workflows/weekly-reference-impl-sync.lock.yml)
-Auto-generated compiled workflow produced by `gh aw compile` from the corresponding `.md` source. This is the agentic counterpart that actually executes the upstream merge using the `gh-aw` MCP server and Copilot coding agent.
+Auto-generated compiled workflow produced by `gh aw compile` from the corresponding `.md` source. This is the agentic counterpart that actually executes the reference implementation merge using the `gh-aw` MCP server and Copilot coding agent.
> **Do not edit this file directly.** Edit the `.md` source and run `gh aw compile`.
---
+## Codegen Check
+
+**File:** [`codegen-check.yml`](workflows/codegen-check.yml)
+
+Verifies that the generated Java source files in `src/generated/java/` are up-to-date with the JSON schemas distributed in the `@github/copilot` npm package.
+
+Steps:
+1. Installs the codegen dependencies from `scripts/codegen/`
+2. Runs the Java code generator (`npm run generate`)
+3. Fails with a diff if any generated file differs from what is committed
+
+Run this locally with:
+```bash
+cd scripts/codegen && npm ci && npm run generate
+```
+
+If changes appear, commit the updated generated files.
+
+---
+
+## Update @github/copilot Dependency
+
+**File:** [`update-copilot-dependency.yml`](workflows/update-copilot-dependency.yml)
+
+Manual workflow triggered when a new version of the `@github/copilot` npm package is published. Accepts a `version` input (e.g. `1.0.25`).
+
+Steps:
+1. Updates `@github/copilot` in `scripts/codegen/package.json`
+2. Re-runs the Java code generator
+3. Commits the updated `package.json`, `package-lock.json`, and all regenerated Java files
+4. Opens (or updates) a pull request for review
+
+The resulting PR will be automatically validated by the **Codegen Check** workflow.
+
+---
+
## Copilot Setup Steps
**File:** [`copilot-setup-steps.yml`](workflows/copilot-setup-steps.yml)
@@ -98,3 +136,4 @@ Sets up:
- JDK 17 (Temurin)
- `gh-aw` CLI extension
- Maven cache
+
diff --git a/docs/adr/adr-001-semver-pre-general-availability.md b/docs/adr/adr-001-semver-pre-general-availability.md
index 44c9cf6db..25008b0f5 100644
--- a/docs/adr/adr-001-semver-pre-general-availability.md
+++ b/docs/adr/adr-001-semver-pre-general-availability.md
@@ -20,8 +20,9 @@ We took an architectural decision to enable us to pursue investigating this poss
Chosen option: "Track SemVer of reference implementation, with one exception.", because this enables us to pursue Virtual Threads without delaying the first public release of `copilot-sdk-java`. Also, we're supposed to be aggressively modernizing our customers.
-To some extent, I would use qualifiers to mark a release as having some feature that is awaiting an upstream full release before it goes full ga, i.e you put out 0.1.46-virtualthreads.3 until upstream is ready to move to 0.2.0 then you release your virtual threads change and go 0.2.0. So I would make your agreement that your version numbers would match with the exception of qualifiers that you might add in exceptional circumstances.
+To some extent, I would use qualifiers to mark a release as having some feature that is awaiting a reference implementation full release before it goes full ga, i.e you put out 0.1.46-virtualthreads.3 until reference implementation is ready to move to 0.2.0 then you release your virtual threads change and go 0.2.0. So I would make your agreement that your version numbers would match with the exception of qualifiers that you might add in exceptional circumstances.
## Related work items
- https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2745172
+
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 7ef3d8ec8..6a0b54f22 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
@@ -6,7 +6,7 @@ Releases of this implementation track releases of the reference implementation.
## Considered Options
-- 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 upstream bare version.
+- 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
@@ -54,3 +54,4 @@ Everything looks healthy. Here's the status:
## Related work items
- https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2766089
+
diff --git a/instructions/copilot-sdk-java.instructions.md b/instructions/copilot-sdk-java.instructions.md
index bf18a3c5a..7881322fd 100644
--- a/instructions/copilot-sdk-java.instructions.md
+++ b/instructions/copilot-sdk-java.instructions.md
@@ -6,7 +6,7 @@ name: 'GitHub Copilot SDK Java Instructions'
## Core Principles
-- The SDK is in technical preview and may have breaking changes
+- The SDK is in public preview and may have breaking changes
- Requires Java 17 or later
- Requires GitHub Copilot CLI installed and in PATH
- Uses `CompletableFuture` for all async operations
diff --git a/jbang-example.java b/jbang-example.java
index 3d02653c1..f075df2f4 100644
--- a/jbang-example.java
+++ b/jbang-example.java
@@ -1,8 +1,8 @@
!
-//DEPS com.github:copilot-sdk-java:${project.version}
+//DEPS com.github:copilot-sdk-java:0.2.2-java.1
import com.github.copilot.sdk.CopilotClient;
-import com.github.copilot.sdk.events.AssistantMessageEvent;
-import com.github.copilot.sdk.events.SessionUsageInfoEvent;
+import com.github.copilot.sdk.generated.AssistantMessageEvent;
+import com.github.copilot.sdk.generated.SessionUsageInfoEvent;
import com.github.copilot.sdk.json.MessageOptions;
import com.github.copilot.sdk.json.PermissionHandler;
import com.github.copilot.sdk.json.SessionConfig;
@@ -28,9 +28,9 @@ public static void main(String[] args) throws Exception {
session.on(SessionUsageInfoEvent.class, usage -> {
var data = usage.getData();
out.println("\n--- Usage Metrics ---");
- out.println("Current tokens: " + (int) data.currentTokens());
- out.println("Token limit: " + (int) data.tokenLimit());
- out.println("Messages count: " + (int) data.messagesLength());
+ out.println("Current tokens: " + data.currentTokens().intValue());
+ out.println("Token limit: " + data.tokenLimit().intValue());
+ out.println("Messages count: " + data.messagesLength().intValue());
});
// Send a message
diff --git a/pom.xml b/pom.xml
index eee587375..562b77ed5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
com.githubcopilot-sdk-java
- 0.2.2-java.0-SNAPSHOT
+ 0.2.3-java.1-SNAPSHOTjarGitHub Copilot SDK :: Java
@@ -51,6 +51,8 @@
${copilot.sdk.clone.dir}/testfalse
+
+
@@ -89,7 +91,7 @@
org.mockitomockito-core
- 5.17.0
+ 5.23.0test
@@ -245,20 +247,43 @@
maven-surefire-plugin3.5.4
-
- ${testExecutionAgentArgs}
+
+ ${testExecutionAgentArgs} ${surefire.jvm.args}${copilot.tests.dir}${copilot.sdk.clone.dir}
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.6.1
+
+
+ add-generated-source
+ generate-sources
+
+ add-source
+
+
+
+ ${project.basedir}/src/generated/java
+
+
+
+
+ com.diffplug.spotlessspotless-maven-plugin2.44.5
+
+ src/generated/java/**/*.java
+ 4.33
@@ -543,6 +568,18 @@
+
+
+ jdk21+
+
+ [21,)
+
+
+ -XX:+EnableDynamicAgentLoading
+
+ skip-test-harness
@@ -613,5 +650,103 @@
+
+
+ update-schemas-from-npm-artifact
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.6.2
+
+
+ update-copilot-schema-version
+ generate-sources
+
+ exec
+
+
+ npm
+ ${project.basedir}/scripts/codegen
+
+ install
+ @github/copilot@${copilot.schema.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.5.0
+
+
+ require-schema-version
+ validate
+
+ enforce
+
+
+
+
+ copilot.schema.version
+ You must specify -Dcopilot.schema.version=VERSION (e.g. 1.0.25)
+
+
+
+
+
+
+
+
+
+
+
+ codegen
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.6.2
+
+
+ codegen-npm-install
+ generate-sources
+
+ exec
+
+
+ npm
+ ${project.basedir}/scripts/codegen
+
+ ci
+
+
+
+
+ codegen-generate
+ generate-sources
+
+ exec
+
+
+ npm
+ ${project.basedir}/scripts/codegen
+
+ run
+ generate
+
+
+
+
+
+
+
+
diff --git a/scripts/codegen/.gitignore b/scripts/codegen/.gitignore
new file mode 100644
index 000000000..c2658d7d1
--- /dev/null
+++ b/scripts/codegen/.gitignore
@@ -0,0 +1 @@
+node_modules/
diff --git a/scripts/codegen/java.ts b/scripts/codegen/java.ts
new file mode 100644
index 000000000..7c2ea7091
--- /dev/null
+++ b/scripts/codegen/java.ts
@@ -0,0 +1,1338 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+/**
+ * Java code generator for session-events and RPC types.
+ * Generates Java source files under src/generated/java/ from JSON Schema files.
+ */
+
+import fs from "fs/promises";
+import path from "path";
+import { fileURLToPath } from "url";
+import type { JSONSchema7 } from "json-schema";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+/** Root of the copilot-sdk-java repo */
+const REPO_ROOT = path.resolve(__dirname, "../..");
+
+/** Event types to exclude from generation (internal/legacy types) */
+const EXCLUDED_EVENT_TYPES = new Set(["session.import_legacy"]);
+
+const AUTO_GENERATED_HEADER = `// AUTO-GENERATED FILE - DO NOT EDIT`;
+const GENERATED_FROM_SESSION_EVENTS = `// Generated from: session-events.schema.json`;
+const GENERATED_FROM_API = `// Generated from: api.schema.json`;
+const GENERATED_ANNOTATION = `@javax.annotation.processing.Generated("copilot-sdk-codegen")`;
+const COPYRIGHT = `/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n *--------------------------------------------------------------------------------------------*/`;
+
+// ── Naming utilities ─────────────────────────────────────────────────────────
+
+function toPascalCase(name: string): string {
+ return name.split(/[-_.]/).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
+}
+
+function toJavaClassName(typeName: string): string {
+ return typeName.split(/[._]/).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
+}
+
+function toCamelCase(name: string): string {
+ const pascal = toPascalCase(name);
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
+}
+
+function toEnumConstant(value: string): string {
+ return value.toUpperCase().replace(/[-. ]/g, "_");
+}
+
+// ── Schema path resolution ───────────────────────────────────────────────────
+
+async function getSessionEventsSchemaPath(): Promise {
+ const candidates = [
+ path.join(REPO_ROOT, "scripts/codegen/node_modules/@github/copilot/schemas/session-events.schema.json"),
+ path.join(REPO_ROOT, "nodejs/node_modules/@github/copilot/schemas/session-events.schema.json"),
+ ];
+ for (const p of candidates) {
+ try {
+ await fs.access(p);
+ return p;
+ } catch {
+ // try next
+ }
+ }
+ throw new Error("session-events.schema.json not found. Run 'npm ci' in scripts/codegen first.");
+}
+
+async function getApiSchemaPath(): Promise {
+ const candidates = [
+ path.join(REPO_ROOT, "scripts/codegen/node_modules/@github/copilot/schemas/api.schema.json"),
+ path.join(REPO_ROOT, "nodejs/node_modules/@github/copilot/schemas/api.schema.json"),
+ ];
+ for (const p of candidates) {
+ try {
+ await fs.access(p);
+ return p;
+ } catch {
+ // try next
+ }
+ }
+ throw new Error("api.schema.json not found. Run 'npm ci' in scripts/codegen first.");
+}
+
+// ── File writing ─────────────────────────────────────────────────────────────
+
+async function writeGeneratedFile(relativePath: string, content: string): Promise {
+ const fullPath = path.join(REPO_ROOT, relativePath);
+ await fs.mkdir(path.dirname(fullPath), { recursive: true });
+ await fs.writeFile(fullPath, content, "utf-8");
+ console.log(` ✓ ${relativePath}`);
+ return fullPath;
+}
+
+// ── Java type mapping ─────────────────────────────────────────────────────────
+
+interface JavaTypeResult {
+ javaType: string;
+ imports: Set;
+}
+
+function schemaTypeToJava(
+ schema: JSONSchema7,
+ required: boolean,
+ context: string,
+ propName: string,
+ nestedTypes: Map
+): JavaTypeResult {
+ const imports = new Set();
+
+ if (schema.anyOf) {
+ const hasNull = schema.anyOf.some((s) => typeof s === "object" && (s as JSONSchema7).type === "null");
+ const nonNull = schema.anyOf.filter((s) => typeof s === "object" && (s as JSONSchema7).type !== "null");
+ if (nonNull.length === 1) {
+ const result = schemaTypeToJava(nonNull[0] as JSONSchema7, required && !hasNull, context, propName, nestedTypes);
+ return result;
+ }
+ // Multi-branch anyOf: fall through to Object, matching the C# generator's
+ // behavior. Java has no union types, so Object is the correct erasure for
+ // anyOf[string, object] and similar multi-variant schemas.
+ console.warn(`[codegen] ${context}.${propName}: anyOf with ${nonNull.length} non-null branches — falling back to Object`);
+ return { javaType: "Object", imports };
+ }
+
+ if (schema.type === "string") {
+ if (schema.format === "uuid") {
+ imports.add("java.util.UUID");
+ return { javaType: "UUID", imports };
+ }
+ if (schema.format === "date-time") {
+ imports.add("java.time.OffsetDateTime");
+ return { javaType: "OffsetDateTime", imports };
+ }
+ if (schema.enum && Array.isArray(schema.enum)) {
+ const enumName = `${context}${toPascalCase(propName)}`;
+ nestedTypes.set(enumName, {
+ kind: "enum",
+ name: enumName,
+ values: schema.enum as string[],
+ description: schema.description,
+ });
+ return { javaType: enumName, imports };
+ }
+ return { javaType: "String", imports };
+ }
+
+ if (Array.isArray(schema.type)) {
+ const nonNullTypes = schema.type.filter((t) => t !== "null");
+ if (nonNullTypes.length === 1) {
+ const baseSchema = { ...schema, type: nonNullTypes[0] };
+ return schemaTypeToJava(baseSchema as JSONSchema7, required, context, propName, nestedTypes);
+ }
+ }
+
+ if (schema.type === "integer") {
+ // JSON Schema "integer" maps to Long (boxed — always used for records).
+ // Use primitive long for required fields in mutable-bean contexts if needed.
+ return { javaType: required ? "long" : "Long", imports };
+ }
+
+ if (schema.type === "number") {
+ return { javaType: required ? "double" : "Double", imports };
+ }
+
+ if (schema.type === "boolean") {
+ return { javaType: required ? "boolean" : "Boolean", imports };
+ }
+
+ if (schema.type === "array") {
+ const items = schema.items as JSONSchema7 | undefined;
+ if (items) {
+ // Always pass required=false so primitives are boxed (List, not List)
+ const itemResult = schemaTypeToJava(items, false, context, propName + "Item", nestedTypes);
+ imports.add("java.util.List");
+ for (const imp of itemResult.imports) imports.add(imp);
+ return { javaType: `List<${itemResult.javaType}>`, imports };
+ }
+ imports.add("java.util.List");
+ console.warn(`[codegen] ${context}.${propName}: array without typed items — falling back to List