From 2fb3b27dcd96dfce3054964837b850b4b0a6b0a5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 3 Aug 2026 21:07:57 +0000
Subject: [PATCH 1/3] Initial plan
From dfa43a50373755d11d9139c6212466986e07b52a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 3 Aug 2026 21:20:13 +0000
Subject: [PATCH 2/3] Add copilot-native module with linux-x64 classifier JAR
Co-authored-by: edburns <75821+edburns@users.noreply.github.com>
---
.gitignore | 1 +
java/copilot-native/pom.xml | 208 ++++++++++++++++++
java/copilot-native/scripts/fetch-native.mjs | 89 ++++++++
.../native/lib/copilot-runtime.properties | 12 +
java/pom.xml | 6 +-
5 files changed, 313 insertions(+), 3 deletions(-)
create mode 100644 java/copilot-native/pom.xml
create mode 100644 java/copilot-native/scripts/fetch-native.mjs
create mode 100644 java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties
diff --git a/.gitignore b/.gitignore
index 714dd47f52..4d039ac903 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ docs/.validation/
# Java
java/target
java/sdk/target
+java/copilot-native/target
java/smoke-test
java/.classpath
java/.project
diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml
new file mode 100644
index 0000000000..52292bf916
--- /dev/null
+++ b/java/copilot-native/pom.xml
@@ -0,0 +1,208 @@
+
+
+
+ 4.0.0
+
+
+ com.github
+ copilot-sdk-java-parent
+ 1.0.10-preview.0-SNAPSHOT
+ ../pom.xml
+
+
+ com.github
+ copilot-sdk-java-runtime
+ jar
+
+ GitHub Copilot SDK :: Java :: Native Runtime
+ Native runtime binaries for the GitHub Copilot Java SDK, published as per-platform classifier JARs
+
+
+
+ ${project.basedir}/../..
+
+ linux-x64
+ ${project.build.directory}/native-staging
+
+ false
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+
+ fetch-native-linux-x64
+ generate-resources
+
+ exec
+
+
+ node
+
+ ${project.basedir}/scripts/fetch-native.mjs
+ ${copilot.sdk.root}
+ ${copilot.native.staging}
+ ${copilot.native.classifier}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ jar-linux-x64
+ package
+
+ jar
+
+
+ ${copilot.native.classifier}
+ ${copilot.native.staging}/${copilot.native.classifier}
+
+ .version
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ verify-native-jars
+ package
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ true
+
+ central
+ true
+
+
+
+
+
+
+
+
+ skip-native-download
+
+
+ copilot.native.skip.download
+ true
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+ jar-linux-x64
+ none
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ verify-native-jars
+ none
+
+
+
+
+
+
+
+
diff --git a/java/copilot-native/scripts/fetch-native.mjs b/java/copilot-native/scripts/fetch-native.mjs
new file mode 100644
index 0000000000..3a71d4329d
--- /dev/null
+++ b/java/copilot-native/scripts/fetch-native.mjs
@@ -0,0 +1,89 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+/**
+ * Downloads the `runtime.node` native binary for a single platform classifier
+ * and stages it for packaging into a classifier JAR.
+ *
+ * Steps:
+ * 1. Read the pinned version and the SHA-512 `integrity` value for
+ * `@github/copilot-` from `nodejs/package-lock.json`.
+ * 2. `npm pack` that exact version into the staging directory.
+ * 3. Verify the downloaded tarball against the `integrity` value.
+ * 4. Extract `package/prebuilds//runtime.node` to
+ * `//native//runtime.node`.
+ * 5. Write `//native//platform.properties`.
+ *
+ * Usage: node fetch-native.mjs
+ */
+
+import { createHash } from 'node:crypto';
+import { execFileSync } from 'node:child_process';
+import fs from 'node:fs';
+import path from 'node:path';
+
+const [repoRoot, stagingDir, classifier] = process.argv.slice(2);
+
+if (!repoRoot || !stagingDir || !classifier) {
+ console.error('Usage: node fetch-native.mjs ');
+ process.exit(1);
+}
+
+const lockPath = path.join(repoRoot, 'nodejs', 'package-lock.json');
+const packageName = `@github/copilot-${classifier}`;
+const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
+const entry = lock.packages?.[`node_modules/${packageName}`];
+
+if (!entry?.version || !entry?.integrity) {
+ console.error(`Could not find version/integrity for ${packageName} in ${lockPath}`);
+ process.exit(1);
+}
+
+const { version, integrity } = entry;
+if (!integrity.startsWith('sha512-')) {
+ console.error(`Unsupported integrity algorithm for ${packageName}: ${integrity}`);
+ process.exit(1);
+}
+
+const outDir = path.join(stagingDir, classifier);
+const resourceDir = path.join(outDir, 'native', classifier);
+const runtimePath = path.join(resourceDir, 'runtime.node');
+const stampPath = path.join(outDir, '.version');
+
+// Idempotence: skip the download when the staged binary already matches.
+if (fs.existsSync(runtimePath) && fs.existsSync(stampPath) && fs.readFileSync(stampPath, 'utf8').trim() === version) {
+ console.log(`${packageName}@${version} already staged at ${runtimePath}`);
+ process.exit(0);
+}
+
+fs.rmSync(outDir, { recursive: true, force: true });
+fs.mkdirSync(resourceDir, { recursive: true });
+
+console.log(`Downloading ${packageName}@${version} ...`);
+const packOutput = execFileSync('npm', ['pack', `${packageName}@${version}`, '--pack-destination', outDir], {
+ encoding: 'utf8',
+ shell: process.platform === 'win32',
+});
+const tarballName = packOutput.trim().split('\n').pop().trim();
+const tarballPath = path.join(outDir, tarballName);
+
+const actual = `sha512-${createHash('sha512').update(fs.readFileSync(tarballPath)).digest('base64')}`;
+if (actual !== integrity) {
+ console.error(`Integrity verification failed for ${tarballPath}`);
+ console.error(` expected: ${integrity}`);
+ console.error(` actual: ${actual}`);
+ process.exit(1);
+}
+console.log(`Integrity verified (${integrity.slice(0, 20)}...).`);
+
+const memberPath = `package/prebuilds/${classifier}/runtime.node`;
+execFileSync('tar', ['-xzf', tarballPath, '-C', outDir, memberPath], { stdio: 'inherit' });
+fs.renameSync(path.join(outDir, memberPath), runtimePath);
+fs.rmSync(path.join(outDir, 'package'), { recursive: true, force: true });
+fs.rmSync(tarballPath, { force: true });
+
+fs.writeFileSync(path.join(resourceDir, 'platform.properties'), `classifier=${classifier}\nversion=${version}\n`);
+fs.writeFileSync(stampPath, `${version}\n`);
+
+console.log(`Staged ${runtimePath}`);
diff --git a/java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties b/java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties
new file mode 100644
index 0000000000..0f32308984
--- /dev/null
+++ b/java/copilot-native/src/main/resources/native/lib/copilot-runtime.properties
@@ -0,0 +1,12 @@
+# Placeholder marker for the primary (classifier-less) artifact of
+# com.github:copilot-sdk-java-runtime.
+#
+# The real native binaries ship in per-platform classifier JARs
+# (e.g. copilot-sdk-java-runtime--linux-x64.jar) under
+# native//runtime.node. This primary JAR exists only to satisfy
+# Maven Central's requirement for a main artifact and intentionally contains
+# no native binaries.
+#
+# This file is processed by Maven resource filtering.
+placeholder=true
+version=${project.version}
diff --git a/java/pom.xml b/java/pom.xml
index efffeca927..9b4a92294c 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -38,11 +38,11 @@
sdk
+ copilot-native
-
From d216d21de2908f45af44115de1b8feb755729829 Mon Sep 17 00:00:00 2001
From: Ed Burns
Date: Mon, 3 Aug 2026 21:48:08 +0000
Subject: [PATCH 3/3] Address Copilot review: harden cache stamp and add
platform.properties assertion
- Persist integrity hash and binary digest in the .version stamp file so
corrupted binaries or lockfile integrity changes are detected on cache hit.
- Add zipentry assertion for platform.properties in the classifier JAR
structural guard.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
java/copilot-native/pom.xml | 6 ++++++
java/copilot-native/scripts/fetch-native.mjs | 18 ++++++++++++++----
2 files changed, 20 insertions(+), 4 deletions(-)
diff --git a/java/copilot-native/pom.xml b/java/copilot-native/pom.xml
index 52292bf916..de57c313fe 100644
--- a/java/copilot-native/pom.xml
+++ b/java/copilot-native/pom.xml
@@ -132,6 +132,12 @@
+
+
+
+
+
+
diff --git a/java/copilot-native/scripts/fetch-native.mjs b/java/copilot-native/scripts/fetch-native.mjs
index 3a71d4329d..18449badfb 100644
--- a/java/copilot-native/scripts/fetch-native.mjs
+++ b/java/copilot-native/scripts/fetch-native.mjs
@@ -52,9 +52,18 @@ const runtimePath = path.join(resourceDir, 'runtime.node');
const stampPath = path.join(outDir, '.version');
// Idempotence: skip the download when the staged binary already matches.
-if (fs.existsSync(runtimePath) && fs.existsSync(stampPath) && fs.readFileSync(stampPath, 'utf8').trim() === version) {
- console.log(`${packageName}@${version} already staged at ${runtimePath}`);
- process.exit(0);
+// The stamp stores version + integrity + binary digest to ensure a corrupted
+// binary or lockfile integrity change is detected.
+if (fs.existsSync(runtimePath) && fs.existsSync(stampPath)) {
+ const stampLines = fs.readFileSync(stampPath, 'utf8').trim().split('\n');
+ const stampVersion = stampLines[0] || '';
+ const stampIntegrity = stampLines[1] || '';
+ const stampBinaryDigest = stampLines[2] || '';
+ const currentBinaryDigest = `sha512-${createHash('sha512').update(fs.readFileSync(runtimePath)).digest('base64')}`;
+ if (stampVersion === version && stampIntegrity === integrity && stampBinaryDigest === currentBinaryDigest) {
+ console.log(`${packageName}@${version} already staged at ${runtimePath}`);
+ process.exit(0);
+ }
}
fs.rmSync(outDir, { recursive: true, force: true });
@@ -84,6 +93,7 @@ fs.rmSync(path.join(outDir, 'package'), { recursive: true, force: true });
fs.rmSync(tarballPath, { force: true });
fs.writeFileSync(path.join(resourceDir, 'platform.properties'), `classifier=${classifier}\nversion=${version}\n`);
-fs.writeFileSync(stampPath, `${version}\n`);
+const binaryDigest = `sha512-${createHash('sha512').update(fs.readFileSync(runtimePath)).digest('base64')}`;
+fs.writeFileSync(stampPath, `${version}\n${integrity}\n${binaryDigest}\n`);
console.log(`Staged ${runtimePath}`);