Skip to content

Commit f7c41b8

Browse files
committed
Add classical code generation workflow for typed event and RPC classes
Introduces a TypeScript-based code generator (scripts/codegen/java.ts) that reads api.schema.json and session-events.schema.json to produce: - Typed session event classes (sealed hierarchy under AbstractSessionEvent) - Typed RPC wrapper classes (ServerRpc, SessionRpc) for JSON-RPC methods - Jackson-annotated records with @JsonCreator, @JsonProperty, @JsonInclude Migrates the hand-written events package to auto-generated types, wires the generated RPC wrappers into CopilotClient and CopilotSession, and adds comprehensive tests including E2E coverage. Also includes: Windows CLI path resolution fixes, shared TestUtil extraction, lazy getRpc() initialization, race condition fix in SessionEventsE2ETest, and a guard preventing agentic sync from modifying src/generated/java/ files. Fix review comments: getRpc() IllegalStateException, UnknownSessionEvent wire type, anyOf heuristic, remove unused var Agent-Logs-Url: https://github.com/github/copilot-sdk-java/sessions/9b8b782c-22ad-450f-885d-2b11d5808a0c Co-authored-by: edburns <75821+edburns@users.noreply.github.com> Revert "Fix review comments: getRpc() IllegalStateException, UnknownSessionEvent wire type, anyOf heuristic, remove unused var" This reverts commit ef1de83. Fix Big Risk 1. `sendExpandedToolResult()` bypasses the typed wrapper (HIGH) scripts/codegen/java.ts - Remove the special-case `anyOf` rule that picked `String` when exactly 2 non-null branches included a string type. Multi-branch `anyOf` now falls through to `Object`, matching the C# reference generator's behavior. src/generated/java/com/github/copilot/sdk/generated/rpc/SessionToolsHandlePendingToolCallParams.java - Change the `result` field type from `String` to `Object` (regenerated output reflecting the `anyOf` rule fix in `java.ts`). src/main/java/com/github/copilot/sdk/CopilotSession.java - Replace the `sendExpandedToolResult(requestId, toolResult)` call with a direct typed-wrapper call: `getRpc().tools.handlePendingToolCall(new SessionToolsHandlePendingToolCallParams(...))`. - Delete the `sendExpandedToolResult()` private method and its Javadoc block, which are no longer needed now that the `result` field accepts `Object`. Signed-off-by: Ed Burns <edburns@microsoft.com> On branch copilot/add-classical-code-gen-workflow-ready-for-review modified: scripts/codegen/java.ts - Put `visible = true` on `SessionEventEvent`. - Add `type` property. on `UnknownSessionEvent`. modified: src/generated/java/com/github/copilot/sdk/generated/SessionEvent.java modified: src/generated/java/com/github/copilot/sdk/generated/UnknownSessionEvent.java - Regenerated. modified: src/main/java/com/github/copilot/sdk/CopilotSession.java - Use Double Check Locked to fix Big Risk #2 2. Lazy `SessionRpc` initialization is not thread-safe (HIGH) modified: src/test/java/com/github/copilot/sdk/ForwardCompatibilityTest.java modified: src/test/java/com/github/copilot/sdk/SessionEventDeserializationTest.java - Refine tests based on changes. Signed-off-by: Ed Burns <edburns@microsoft.com> On branch copilot/add-classical-code-gen-workflow-ready-for-review modified: pom.xml - Add profiles for generating code and updating the schemas from which the code is generated. modified: scripts/codegen/java.ts - Address copilot comment: > requiredSet is computed but never used in renderNestedType(), which makes the generator harder to maintain (and can confuse future changes around nullability/boxing). Remove it or use it to drive required-vs-optional component typing if that’s the intent. Signed-off-by: Ed Burns <edburns@microsoft.com> Add AI code review to update-copilot-dependency workflow
1 parent d892bdb commit f7c41b8

File tree

329 files changed

+13313
-3485
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

329 files changed

+13313
-3485
lines changed

.gitattributes

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
.github/workflows/*.lock.yml linguist-generated=true merge=ours
1+
.github/workflows/*.lock.yml linguist-generated=true merge=ours
2+
src/generated/java/** eol=lf linguist-generated=true

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ mvn test -Dtest=CopilotClientTest
7777

7878
- `com.github.copilot.sdk` - Core classes (CopilotClient, CopilotSession, JsonRpcClient)
7979
- `com.github.copilot.sdk.json` - DTOs, request/response types, handler interfaces (SessionConfig, MessageOptions, ToolDefinition, etc.)
80-
- `com.github.copilot.sdk.events` - Event types for session streaming (AssistantMessageEvent, SessionIdleEvent, ToolExecutionStartEvent, etc.)
80+
- `com.github.copilot.sdk.generated` - Generated event types for session streaming (SessionEvent, AssistantMessageEvent, SessionIdleEvent, ToolExecutionStartEvent, etc.)
8181

8282
### Test Infrastructure
8383

.github/prompts/agentic-merge-reference-impl.prompt.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,43 @@
22

33
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.
44

5+
## ❌❌❌ ABSOLUTE PROHIBITION: DO NOT TOUCH GENERATED CODE ❌❌❌
6+
7+
> ### 🚫 THE FILES UNDER `src/generated/java/` ARE FORBIDDEN 🚫
8+
>
9+
> **NEVER, UNDER ANY CIRCUMSTANCES, MODIFY ANY FILE IN `src/generated/java/`.**
10+
>
11+
> These files are AUTO-GENERATED by `scripts/codegen/java.ts` and MUST NOT be hand-edited.
12+
> They are regenerated automatically when the `@github/copilot` npm package version is updated.
13+
>
14+
> ❌ DO NOT edit `src/generated/java/**/*.java`
15+
> ❌ DO NOT create new files in `src/generated/java/`
16+
> ❌ DO NOT delete files from `src/generated/java/`
17+
> ❌ DO NOT "fix" or "improve" generated code — it will be overwritten
18+
>
19+
> **IF ANY CHANGE YOU NEED TO MAKE REQUIRES TOUCHING `src/generated/java/`:**
20+
>
21+
> 1. **STOP IMMEDIATELY** — do not make the change
22+
> 2. **FAIL the agentic sync** — do not attempt to work around this restriction
23+
> 3. **Push an explanatory commit** with a message such as:
24+
> ```
25+
> SYNC BLOCKED: Required change needs generated code update
26+
>
27+
> The reference implementation change '<description>' requires updates
28+
> to the generated RPC/event types in src/generated/java/. These files
29+
> cannot be hand-edited — they must be regenerated.
30+
>
31+
> ACTION REQUIRED: Re-run the update-copilot-dependency.yml workflow
32+
> to update the @github/copilot npm package and regenerate the Java
33+
> source files before this sync can be completed.
34+
> ```
35+
> 4. **Document in the PR body** which reference implementation changes were blocked and why
36+
> 5. **Do NOT attempt to work around this restriction** by making equivalent changes elsewhere
37+
>
38+
> The correct way to update generated code is:
39+
> - Trigger the `update-copilot-dependency.yml` workflow with the new `@github/copilot` version
40+
> - That workflow updates `package.json`, regenerates all files in `src/generated/java/`, and opens a PR
41+
542
## ⚠️ IMPORTANT: Java SDK Design Takes Priority
643
744
**The current design and architecture of the Java SDK is the priority.** When porting changes from the reference implementation:
@@ -101,7 +138,7 @@ For each change in the reference implementation diff, determine:
101138
| `dotnet/src/Client.cs` | `src/main/java/com/github/copilot/sdk/CopilotClient.java` |
102139
| `dotnet/src/Session.cs` | `src/main/java/com/github/copilot/sdk/CopilotSession.java` |
103140
| `dotnet/src/Types.cs` | `src/main/java/com/github/copilot/sdk/types/*.java` |
104-
| `dotnet/src/Generated/*.cs` | `src/main/java/com/github/copilot/sdk/types/*.java` |
141+
| `dotnet/src/Generated/*.cs` | **DO NOT TOUCH** `src/generated/java/**` — see top of this file |
105142
| `dotnet/test/*.cs` | `src/test/java/com/github/copilot/sdk/*Test.java` |
106143
| `docs/getting-started.md` | `README.md` and `src/site/markdown/*.md` |
107144
| `docs/*.md` (new files) | `src/site/markdown/*.md` + update `src/site/site.xml` |
@@ -111,6 +148,10 @@ For each change in the reference implementation diff, determine:
111148
112149
## Step 5: Apply Changes to Java SDK
113150

151+
> ### ❌❌❌ REMINDER: `src/generated/java/` IS FORBIDDEN ❌❌❌
152+
> Any change that requires modifying `src/generated/java/` MUST stop the sync.
153+
> See the **ABSOLUTE PROHIBITION** section at the top of this file for required actions.
154+
114155
When porting changes:
115156

116157
### ⚠️ Priority: Preserve Java SDK Design
@@ -400,6 +441,7 @@ Before finishing:
400441

401442
## Checklist
402443

444+
- [ ]**VERIFIED: No files in `src/generated/java/` were modified** (if any were needed, sync was stopped per ABSOLUTE PROHIBITION above)
403445
- [ ] New branch created from `main`
404446
- [ ] Copilot CLI updated to latest version
405447
- [ ] README.md updated with minimum CLI version requirement
@@ -430,6 +472,7 @@ Before finishing:
430472

431473
## Notes
432474

475+
- ❌❌❌ **`src/generated/java/` IS FORBIDDEN** — NEVER modify generated files; re-run `update-copilot-dependency.yml` instead ❌❌❌
433476
- The reference implementation SDK is at: `https://github.com/github/copilot-sdk.git`
434477
- Primary reference implementation is in `dotnet/` folder
435478
- This Java SDK targets Java 17+

.github/prompts/coding-agent-merge-reference-impl-instructions.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,17 @@ Add the 'reference-impl-sync' label to the existing PR by running this command i
1717
If after analyzing the reference implementation diff there are no relevant changes to port to the Java SDK,
1818
push an empty commit with a message explaining why no changes were needed, so the PR reflects
1919
the analysis outcome. The repository maintainer will close the PR and issue manually.
20+
21+
❌❌❌ ABSOLUTE PROHIBITION ❌❌❌
22+
23+
NEVER MODIFY ANY FILE UNDER src/generated/java/ — THESE FILES ARE AUTO-GENERATED AND FORBIDDEN.
24+
25+
If any change requires modifying src/generated/java/:
26+
1. STOP IMMEDIATELY — do not make the change
27+
2. FAIL the sync with an explanatory commit message
28+
3. Instruct the maintainer to re-run update-copilot-dependency.yml to regenerate these files
29+
30+
See the ABSOLUTE PROHIBITION section in .github/prompts/agentic-merge-reference-impl.prompt.md
31+
for the full required procedure and commit message template.
32+
33+
❌❌❌ END ABSOLUTE PROHIBITION ❌❌❌
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: "Codegen Check"
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
paths:
9+
- 'scripts/codegen/**'
10+
- 'src/generated/java/**'
11+
- '.github/workflows/codegen-check.yml'
12+
workflow_dispatch:
13+
14+
permissions:
15+
contents: read
16+
17+
jobs:
18+
check:
19+
name: "Verify generated files are up-to-date"
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
23+
24+
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
25+
with:
26+
node-version: 22
27+
28+
- name: Install codegen dependencies
29+
working-directory: ./scripts/codegen
30+
run: npm ci
31+
32+
- name: Run codegen
33+
working-directory: ./scripts/codegen
34+
run: npm run generate
35+
36+
- name: Check for uncommitted changes
37+
run: |
38+
if [ -n "$(git status --porcelain)" ]; then
39+
echo "::error::Generated files are out of date. Run 'cd scripts/codegen && npm run generate' and commit the changes."
40+
git diff --stat
41+
git diff
42+
exit 1
43+
fi
44+
echo "✅ Generated files are up-to-date"
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
name: "Update @github/copilot Dependency"
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: 'Target version of @github/copilot (e.g. 1.0.24)'
8+
required: true
9+
type: string
10+
11+
permissions:
12+
contents: write
13+
pull-requests: write
14+
15+
jobs:
16+
update:
17+
name: "Update @github/copilot to ${{ inputs.version }}"
18+
runs-on: ubuntu-latest
19+
steps:
20+
- name: Validate version input
21+
env:
22+
VERSION: ${{ inputs.version }}
23+
run: |
24+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9._-]+)?$ ]]; then
25+
echo "::error::Invalid version format '$VERSION'. Expected semver (e.g. 1.0.24)."
26+
exit 1
27+
fi
28+
29+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
30+
31+
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
32+
with:
33+
node-version: 22
34+
35+
- name: Update @github/copilot in scripts/codegen
36+
env:
37+
VERSION: ${{ inputs.version }}
38+
working-directory: ./scripts/codegen
39+
# npm install updates package.json and package-lock.json to the new
40+
# version; npm ci (below) then does a clean, reproducible install from
41+
# the updated lock file. Both steps are required: npm install alone
42+
# leaves leftover packages, while npm ci alone cannot change the pinned
43+
# version in the lock file.
44+
run: npm install "@github/copilot@$VERSION"
45+
46+
- name: Install codegen dependencies
47+
working-directory: ./scripts/codegen
48+
run: npm ci
49+
50+
- name: Run codegen
51+
working-directory: ./scripts/codegen
52+
run: npm run generate
53+
54+
- uses: ./.github/actions/setup-copilot
55+
56+
- name: Verify generated code against schema
57+
env:
58+
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
59+
run: |
60+
cat > /tmp/verify-codegen-prompt.txt << 'PROMPT_EOF'
61+
You are running inside the copilot-sdk-java repository.
62+
The code generator has just run and produced Java source files under
63+
src/generated/java/com/github/copilot/sdk/generated/rpc/.
64+
65+
Your task is to spot-check the generated API classes against the source
66+
JSON schema to verify the generator produced correct output.
67+
68+
**Critical constraint: Do NOT modify any files. This is a read-only
69+
verification. Use only read/inspect operations.**
70+
71+
**Steps:**
72+
1. Read the API schema at:
73+
scripts/codegen/node_modules/@github/copilot/schemas/api.schema.json
74+
75+
2. Pick these 3 generated API classes to verify:
76+
- src/generated/java/com/github/copilot/sdk/generated/rpc/SessionToolsApi.java
77+
- src/generated/java/com/github/copilot/sdk/generated/rpc/SessionUiApi.java
78+
- src/generated/java/com/github/copilot/sdk/generated/rpc/SessionPermissionsApi.java
79+
80+
3. For each class, verify:
81+
- Every RPC method in the schema's corresponding namespace has a
82+
matching Java method in the generated class
83+
- Parameter record fields match the JSON schema property names
84+
- Java types are consistent with JSON schema types (String for
85+
"string", Integer/int for "integer", Boolean/boolean for
86+
"boolean", List for "array", Map or a generated class for
87+
"object", etc.)
88+
- No extra methods exist in the Java class that are not in the
89+
schema
90+
91+
4. Print a summary table of what was checked and the result for each
92+
method.
93+
94+
5. If ANY mismatch is found, print the details and exit with a
95+
non-zero code.
96+
If all checks pass, print "Schema verification passed" and exit 0.
97+
PROMPT_EOF
98+
99+
copilot --yolo --prompt "$(cat /tmp/verify-codegen-prompt.txt)"
100+
101+
- name: Create pull request
102+
env:
103+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
104+
VERSION: ${{ inputs.version }}
105+
run: |
106+
BRANCH="update-copilot-$VERSION"
107+
git config user.name "github-actions[bot]"
108+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
109+
110+
if git rev-parse --verify "origin/$BRANCH" >/dev/null 2>&1; then
111+
git checkout "$BRANCH"
112+
git reset --hard HEAD
113+
else
114+
git checkout -b "$BRANCH"
115+
fi
116+
117+
git add -A
118+
119+
if git diff --cached --quiet; then
120+
echo "No changes detected; skipping commit and PR creation."
121+
exit 0
122+
fi
123+
124+
git commit -m "Update @github/copilot to $VERSION
125+
126+
- Updated @github/copilot in scripts/codegen
127+
- Re-ran Java code generator"
128+
git push origin "$BRANCH" --force-with-lease
129+
130+
if gh pr view "$BRANCH" >/dev/null 2>&1; then
131+
echo "Pull request for branch '$BRANCH' already exists; updated branch only."
132+
else
133+
gh pr create \
134+
--title "Update @github/copilot to $VERSION" \
135+
--body "Automated update of \`@github/copilot\` to version \`$VERSION\`.
136+
137+
### Changes
138+
- Updated \`@github/copilot\` in \`scripts/codegen/package.json\`
139+
- Re-ran Java code generator (\`scripts/codegen\`)
140+
141+
> Created by the **Update @github/copilot Dependency** workflow." \
142+
--base main \
143+
--head "$BRANCH"
144+
fi

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ examples-test/
55
blog-copilotsdk/
66
.claude/worktrees
77
smoke-test
8-
*job-logs.txt
8+
*job-logs.txt*
99
temporary-prompts/
1010
changebundle.txt*
1111
.classpath
1212
.project
1313
.settings
14+
scripts/codegen/node_modules/
1415
*~

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ implementation 'com.github:copilot-sdk-java:0.2.2-java.1'
6767

6868
```java
6969
import com.github.copilot.sdk.CopilotClient;
70-
import com.github.copilot.sdk.events.AssistantMessageEvent;
71-
import com.github.copilot.sdk.events.SessionUsageInfoEvent;
70+
import com.github.copilot.sdk.generated.AssistantMessageEvent;
71+
import com.github.copilot.sdk.generated.SessionUsageInfoEvent;
7272
import com.github.copilot.sdk.json.CopilotClientOptions;
7373
import com.github.copilot.sdk.json.MessageOptions;
7474
import com.github.copilot.sdk.json.PermissionHandler;

config/checkstyle/checkstyle.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
<property name="charset" value="UTF-8"/>
1212
<property name="severity" value="error"/>
1313

14-
<!-- Exclude json package and events package (self-documenting DTOs) -->
14+
<!-- Exclude json package, events package, and generated package (self-documenting DTOs) -->
1515
<module name="BeforeExecutionExclusionFileFilter">
16-
<property name="fileNamePattern" value=".*[\\/](json|events)[\\/].*\.java$"/>
16+
<property name="fileNamePattern" value=".*[\\/](json|events|generated|rpc)[\\/].*\.java$"/>
1717
</module>
1818

1919
<module name="TreeWalker">

config/spotbugs/spotbugs-exclude.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
<!--
33
SpotBugs exclusion filter for the Copilot SDK.
44
5-
The 'events' and 'json' packages contain Jackson-deserialized DTOs where
5+
The 'generated' and 'json' packages contain Jackson-deserialized DTOs where
66
returning mutable internal representations (EI_EXPOSE_REP) and storing
77
references to mutable objects (EI_EXPOSE_REP2) is intentional and expected.
88
Making defensive copies would add overhead without meaningful security
99
benefit for these read-only data transfer objects.
1010
-->
1111
<FindBugsFilter>
1212
<Match>
13-
<Package name="com.github.copilot.sdk.events"/>
13+
<Package name="com.github.copilot.sdk.generated"/>
1414
<Bug pattern="EI_EXPOSE_REP,EI_EXPOSE_REP2"/>
1515
</Match>
1616
<Match>

0 commit comments

Comments
 (0)