Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b6ecca4
Use Copilot CLI releases for Node runtime
devm33 Sep 1, 2026
6ba8a5f
Address CLI release runtime review findings
devm33 Sep 1, 2026
2b3a448
Package CLI runtimes by platform
devm33 Sep 1, 2026
70abcae
Use release checksum manifests
devm33 Sep 1, 2026
5e5fb70
Fix cross-platform release runtime CI
devm33 Sep 1, 2026
5c90984
Address release runtime code scanning
devm33 Sep 1, 2026
df80edd
Fix release runtime CI regressions
devm33 Sep 1, 2026
c6b6178
Fix runtime packaging integration gaps
devm33 Sep 1, 2026
3b1aa12
Report early CLI exits consistently
devm33 Sep 1, 2026
34e0f6f
Retrigger PR checks
devm33 Sep 1, 2026
c567224
Run npm packaging through Node
devm33 Sep 1, 2026
065a5c7
Resolve runtime path analysis findings
devm33 Sep 1, 2026
e72b8c6
Address final runtime review findings
devm33 Sep 1, 2026
4b439e8
Harden release packaging metadata
devm33 Sep 1, 2026
50d92a5
Avoid unsafe runtime path combination
devm33 Sep 2, 2026
9283e6d
Read pinned CLI version in all Rust jobs
devm33 Sep 2, 2026
3aa1e65
Isolate legacy extension tests on macOS
devm33 Sep 2, 2026
62b213b
Address runtime packaging review feedback
devm33 Sep 2, 2026
efe1379
Split hanging macOS .NET test shard
devm33 Sep 2, 2026
2fa34f9
Timeout stalled CLI release downloads
devm33 Sep 2, 2026
7d0127f
Isolate macOS permission test fixtures
devm33 Sep 2, 2026
88ec8ca
Isolate macOS RPC test fixtures
devm33 Sep 2, 2026
fa054da
Cancel blocked tools during resume tests
devm33 Sep 2, 2026
176ef5f
Use cross-target cancellation wait
devm33 Sep 2, 2026
5f6c062
Stabilize cross-platform E2E cleanup
devm33 Sep 2, 2026
bf51022
Finish CLI runtime rebase
devm33 Sep 2, 2026
7c4801f
Synchronize Go session event shutdown
devm33 Sep 3, 2026
6b286bc
Stabilize event fidelity ordering checks
devm33 Sep 3, 2026
a36e3b6
Wait for disconnected tools to unregister
devm33 Sep 3, 2026
b5ad604
Update rebased CLI release pin
devm33 Sep 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .github/actions/setup-copilot/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,15 @@ runs:
- name: Set CLI path
id: cli-path
run: |
# As of CLI 1.0.64-1 the @github/copilot package is a thin loader; the
# runnable index.js ships in the installed platform package
# (e.g. @github/copilot-linux-x64). Exactly one is installed.
cli_path=$(ls "$(pwd)"/nodejs/node_modules/@github/copilot-*/index.js 2>/dev/null | head -n1)
cli_path=$(npm --prefix "$(pwd)/nodejs" run --silent prepare:runtime -- --print-path)
if [ -z "$cli_path" ]; then
echo "Could not find @github/copilot platform package (index.js) under nodejs/node_modules" >&2
echo "Could not prepare the Copilot CLI runtime" >&2
exit 1
fi
echo "path=$cli_path" >> $GITHUB_OUTPUT
shell: bash
- name: Verify CLI works
run: node ${{ steps.cli-path.outputs.path }} --version
run: |
legacy_cli=$(npm --prefix "$(pwd)/nodejs" run --silent prepare:runtime -- --print-legacy-path)
node "$legacy_cli" --version
shell: bash
4 changes: 2 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
- Tools: each SDK has helper APIs to expose functions as tools; prefer the language's `DefineTool`/`@define_tool`/`CopilotTool.DefineTool` patterns (see language READMEs).
- Infinite sessions are enabled by default and persist workspace state to `~/.copilot/session-state/{sessionId}`; compaction events are emitted (`session.compaction_start`, `session.compaction_complete`). See language READMEs for usage.
- Streaming: when `streaming`/`Streaming=true` you receive delta events (`assistant.message_delta`, `assistant.reasoning_delta`) and final events (`assistant.message`, `assistant.reasoning`) — tests expect this behavior.
- Type generation is centralized in `nodejs/scripts/generate-session-types.ts` and requires the `@github/copilot` schema to be present (often via `npm link` or installed package).
- Type generation is centralized in `scripts/codegen/` and downloads schemas from the pinned `github/copilot-cli` release.
- Java code style: 4-space indent (Spotless + Eclipse formatter), fluent setter pattern for config classes, Javadoc required on public APIs (enforced by Checkstyle, except `json`/`events` packages).
- Java handlers return `CompletableFuture` (the Java equivalent of C# `async/await`). When porting from .NET: convert properties → getters/fluent setters, use Jackson (`ObjectMapper`, `@JsonProperty`) for serialization.

Expand All @@ -64,7 +64,7 @@
- SDK code: `nodejs/src`, `python/copilot`, `go`, `dotnet/src`, `rust/src`, `java/sdk/src/main/java`
- Unit tests: `nodejs/test`, `python/*`, `go/*`, `dotnet/test`, `rust/tests`, `java/sdk/src/test/java`
- E2E tests: `*/e2e/` folders that use the shared replay proxy and `test/snapshots/`, `java/sdk/src/test/java/**/e2e/`
- Generated types: update schema in `@github/copilot` then run `cd nodejs && npm run generate:session-types` and commit generated files in `src/generated` or language generated location. Java generated types: `java/sdk/src/generated/java`
- Generated types: update the pinned Copilot CLI version, run `cd nodejs && npm run generate`, and commit generated files in each language's generated location. Java generated types: `java/sdk/src/generated/java`

## Boundaries — files you must NOT hand-edit ⛔

Expand Down
118 changes: 71 additions & 47 deletions .github/workflows/dotnet-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,43 +104,65 @@ jobs:
- os: macos-latest
transport: default
backend: capi
shard: "2b1a-permission-provider"
shard: "2b-pending"
- os: macos-latest
transport: default
backend: capi
shard: "2b1a-rewind"
shard: "2b-permission"
- os: macos-latest
transport: default
backend: capi
shard: "2b1a-rpc-a"
shard: "2b-auth"
- os: macos-latest
transport: default
backend: capi
shard: "2b1a-rpc-e"
shard: "2b-hooks"
- os: macos-latest
transport: default
backend: capi
shard: "2b1b-mcp-and-skills"
shard: "2b-unit-p"
- os: macos-latest
transport: default
backend: capi
shard: "2b1b-mcp-config"
shard: "2b-provider"
- os: macos-latest
transport: default
backend: capi
shard: "2b1b-mcp-lifecycle"
shard: "2b-rpc-additional"
- os: macos-latest
transport: default
backend: capi
shard: "2b1b-queue"
shard: "2b-rpc-agent"
- os: macos-latest
transport: default
backend: capi
shard: "2b1b-remote"
shard: "2b-rpc-event-log"
- os: macos-latest
transport: default
backend: capi
shard: "2b2"
shard: "2b-rpc-event-effects"
- os: macos-latest
transport: default
backend: capi
shard: "2b-rpc-mcp-skills"
- os: macos-latest
transport: default
backend: capi
shard: "2b-rpc-mcp-config"
- os: macos-latest
transport: default
backend: capi
shard: "2b-rpc-mcp-lifecycle"
- os: macos-latest
transport: default
backend: capi
shard: "2b-rpc-q-z"
# Standalone extension tests require the legacy JavaScript entrypoint.
# Keep its subprocess tree isolated from the rest of the O/P/R shard.
- os: macos-latest
transport: default
backend: capi
shard: extensions
- os: macos-latest
transport: default
backend: capi
Expand Down Expand Up @@ -223,7 +245,6 @@ jobs:
)

filter="$DOTNET_TEST_FILTER"
individual_filters=()
if [[ "$DOTNET_TEST_SHARD" != "full" ]]; then
case "$DOTNET_TEST_SHARD" in
1)
Expand All @@ -240,69 +261,72 @@ jobs:
initials=(B E F G)
shard_filter=""
;;
2b1a-permission-provider)
2b-pending)
initials=()
shard_filter=""
individual_filters=(
"FullyQualifiedName~GitHub.Copilot.Test.E2E.PendingWorkResumeE2ETests"
"FullyQualifiedName~GitHub.Copilot.Test.E2E.PerSessionAuthE2ETests"
"FullyQualifiedName~GitHub.Copilot.Test.E2E.PermissionE2ETests"
"FullyQualifiedName~GitHub.Copilot.Test.E2E.PreMcpToolCallHookE2ETests"
"FullyQualifiedName~GitHub.Copilot.Test.E2E.ProviderEndpointE2ETests"
"FullyQualifiedName~GitHub.Copilot.Test.Unit.PermissionHandlerTests"
"FullyQualifiedName~GitHub.Copilot.Test.Unit.PublicDtoTests"
)
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.PendingWorkResumeE2ETests"
;;
2b1a-rewind)
2b-permission)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.Rewind"
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.PermissionE2ETests"
;;
2b1a-rpc-a)
2b-auth)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcA"
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.PerSessionAuthE2ETests"
;;
2b1a-rpc-e)
2b-hooks)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcE"
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.PreMcpToolCallHookE2ETests"
;;
2b1b-mcp-and-skills)
2b-unit-p)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.Unit.P"
;;
2b-provider)
initials=(O)
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.ProviderEndpointE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RewindE2ETests|FullyQualifiedName~GitHub.Copilot.Test.Unit.R"
;;
2b-rpc-additional)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcAdditionalEdgeCasesE2ETests"
;;
2b-rpc-agent)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcAgentE2ETests"
;;
2b-rpc-event-log)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcEventLogE2ETests"
;;
2b-rpc-event-effects)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcEventSideEffectsE2ETests"
;;
2b-rpc-mcp-skills)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcMcpAndSkillsE2ETests"
;;
2b1b-mcp-config)
2b-rpc-mcp-config)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcMcpConfigE2ETests"
;;
2b1b-mcp-lifecycle)
2b-rpc-mcp-lifecycle)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcMcpLifecycleE2ETests"
;;
2b1b-queue)
2b-rpc-q-z)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcQ"
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcQueueE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcRemoteE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcScheduleE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcServerE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcServerMiscE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcServerPluginsE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcServerRemoteControlE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcSessionStateE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcSessionStateExtrasE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcShellAndFleetE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcShellEdgeCaseE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcShellUserRequestedE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcTasksAndHandlersE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcUiEphemeralQueryE2ETests|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcWorkspaceCheckpointsE2ETests"
;;
2b1b-remote)
extensions)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcR"
;;
2b2)
initials=()
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcS|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcT|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcU|FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcW|FullyQualifiedName~GitHub.Copilot.Test.Unit.R"
shard_filter="FullyQualifiedName~GitHub.Copilot.Test.E2E.RpcExtensionsLoadedE2ETests"
;;
2c)
initials=(T V X Z)
shard_filter=""
;;
esac

if (( ${#individual_filters[@]} > 0 )); then
for individual_filter in "${individual_filters[@]}"; do
combined_filter="${filter:+(${filter})&}(${individual_filter})"
dotnet test test/GitHub.Copilot.SDK.Test.csproj "${args[@]}" --filter "$combined_filter"
done
exit 0
fi

for namespace in E2E Unit; do
for initial in "${initials[@]}"; do
clause="FullyQualifiedName~GitHub.Copilot.Test.${namespace}.${initial}"
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/java-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,12 @@ jobs:
run: mvn javadoc:javadoc -q

- name: Verify CLI works
run: node ../nodejs/node_modules/@github/copilot/npm-loader.js --version
run: |
npm --prefix ../nodejs ci --ignore-scripts
cli_path=$(npm --prefix ../nodejs run --silent prepare:runtime -- --print-path)
test -x "$cli_path"
legacy_cli=$(npm --prefix ../nodejs run --silent prepare:runtime -- --print-legacy-path)
node "$legacy_cli" --version

- name: Run spotless check
if: matrix.test-jdk == '25'
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/nodejs-sdk-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ jobs:
- name: Build SDK
run: npm run build

- name: Build and verify release packages
if: runner.os == 'Linux' && matrix.transport == 'default'
run: |
npm run pack:release
npm run verify:release-packages

- name: Install test harness dependencies
working-directory: ./test/harness
run: npm ci --ignore-scripts
Expand All @@ -58,6 +64,11 @@ jobs:
working-directory: ./test/harness
run: npm test

- name: Prepare Copilot CLI runtime
run: |
runtime_path=$(npm run --silent prepare:runtime -- --print-path)
echo "COPILOT_CLI_PATH=$runtime_path" >> "$GITHUB_ENV"
Comment thread
devm33 marked this conversation as resolved.

- name: Warm up PowerShell
if: runner.os == 'Windows'
run: pwsh.exe -Command "Write-Host 'PowerShell ready'"
Expand Down
60 changes: 45 additions & 15 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,18 @@ jobs:
- name: Build
run: npm run build
- name: Pack
id: pack
run: |
TARBALL="$(npm pack . --json | jq -r '.[0].filename')"
if [ -z "$TARBALL" ] || [ ! -f "$TARBALL" ]; then
echo "::error::npm pack did not produce a tarball."
npm run pack:release
TARBALL_COUNT="$(find . -maxdepth 1 -name 'github-copilot-sdk-*.tgz' | wc -l | tr -d ' ')"
if [ "$TARBALL_COUNT" -ne 9 ]; then
echo "::error::Expected nine Node.js package tarballs, found $TARBALL_COUNT."
exit 1
fi
echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT"
- name: Upload artifact
uses: actions/upload-artifact@v7.0.0
with:
name: nodejs-package
path: nodejs/${{ steps.pack.outputs.tarball }}
path: nodejs/github-copilot-sdk-*.tgz
if-no-files-found: error

publish-nodejs:
Expand Down Expand Up @@ -150,12 +149,29 @@ jobs:
set -euo pipefail
shopt -s nullglob
TARBALLS=(./dist/*.tgz)
if [ "${#TARBALLS[@]}" -ne 1 ]; then
echo "::error::Expected exactly one Node.js package tarball, found ${#TARBALLS[@]}."
if [ "${#TARBALLS[@]}" -ne 9 ]; then
echo "::error::Expected nine Node.js package tarballs, found ${#TARBALLS[@]}."
exit 1
fi
MAIN_TARBALL=""
for TARBALL in "${TARBALLS[@]}"; do
PACKAGE_NAME="$(tar -xOf "$TARBALL" package/package.json | jq -r .name)"
if [ "$PACKAGE_NAME" = "@github/copilot-sdk" ]; then
MAIN_TARBALL="$TARBALL"
continue
fi
node nodejs/scripts/npm-release.js publish \
"$TARBALL" \
"$DIST_TAG" \
https://registry.npmjs.org \
public
done
if [ -z "$MAIN_TARBALL" ]; then
echo "::error::Main @github/copilot-sdk tarball not found."
exit 1
fi
node nodejs/scripts/npm-release.js publish \
"${TARBALLS[0]}" \
"$MAIN_TARBALL" \
"$DIST_TAG" \
https://registry.npmjs.org \
public
Expand Down Expand Up @@ -209,12 +225,29 @@ jobs:
fi
shopt -s nullglob
TARBALLS=(./dist/*.tgz)
if [ "${#TARBALLS[@]}" -ne 1 ]; then
echo "::error::Expected exactly one Node.js package tarball, found ${#TARBALLS[@]}."
if [ "${#TARBALLS[@]}" -ne 9 ]; then
echo "::error::Expected nine Node.js package tarballs, found ${#TARBALLS[@]}."
exit 1
fi
MAIN_TARBALL=""
for TARBALL in "${TARBALLS[@]}"; do
PACKAGE_NAME="$(tar -xOf "$TARBALL" package/package.json | jq -r .name)"
if [ "$PACKAGE_NAME" = "@github/copilot-sdk" ]; then
MAIN_TARBALL="$TARBALL"
continue
fi
node nodejs/scripts/npm-release.js publish \
"$TARBALL" \
"$DIST_TAG" \
"$FEED_URL" \
azure
done
if [ -z "$MAIN_TARBALL" ]; then
echo "::error::Main @github/copilot-sdk tarball not found."
exit 1
fi
node nodejs/scripts/npm-release.js publish \
"${TARBALLS[0]}" \
"$MAIN_TARBALL" \
"$DIST_TAG" \
"$FEED_URL" \
azure
Expand Down Expand Up @@ -327,9 +360,6 @@ jobs:
node-version: "22.x"
- name: Set up uv
uses: astral-sh/setup-uv@v7
- name: Install Node.js dependencies (for CLI version)
working-directory: ./nodejs
run: npm ci --ignore-scripts
- name: Set version
run: sed -i "s/^version = .*/version = \"${{ needs.version.outputs.version }}\"/" pyproject.toml
- name: Inject CLI version
Expand Down
Loading
Loading