Bind Copilot launches to pinned build-owned native assets

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-09-28 15:17:09 -05:00
1 parent fe3b7e6fb8
commit 969944b38e
3 files changed
+103 -1

No files matched your search

@@ -3,11 +3,13 @@ import type { CanonicalProviderEvent } from "../../provider-events.js";
import { redactPaperclipSemanticValue } from "../../semantic-tools/redaction.js";
import { COPILOT_ACP_EVENT_METHOD, normalizeCopilotSessionEvent, type CopilotSessionEvent } from "./copilot-events.js";
import type { AcpxProfileExtensionAdapter, AcpxProfileExtensionContext } from "./profile-extensions.js";
import { classifyCopilotFailure } from "./copilot-profile.js";
/** Display projection only. Native notices cannot settle or charge a runner turn. */
export function createCopilotProfileExtensionAdapter(context: AcpxProfileExtensionContext): AcpxProfileExtensionAdapter {
export function createCopilotProfileExtensionAdapter(context: AcpxProfileExtensionContext): AcpxProfileExtensionAdapter & { classifyError: typeof classifyCopilotFailure } {
let sequence = 0;
return {
classifyError: classifyCopilotFailure,
async request() {
throw new Error("Copilot 1.0.88 has no qualified inbound ACP extension request responder");
},
@@ -0,0 +1,46 @@
import { createHash } from "node:crypto";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { describe, expect, it, vi } from "vitest";
import { verifyNativeAcpxInstallation } from "./installation-integrity.js";
import { COPILOT_CLOSURE_SHA256, verifyCopilotInstallation } from "./copilot-installation.js";
import { COPILOT_LAUNCH_ARGUMENTS } from "./copilot-profile.js";
import { QUALIFIED_ACPX_PROFILES } from "./qualified-profiles.js";
vi.mock("./installation-integrity.js", () => ({ verifyNativeAcpxInstallation: vi.fn() }));
describe("Copilot build-owned installation", () => {
it("pins the materializer's complete executable closure on every target platform", () => {
const script = readFileSync(new URL("../../../scripts/materialize-copilot-binary.mjs", import.meta.url), "utf8");
const records = [...script.matchAll(/"(darwin-arm64|darwin-x64|linux-x64)": Object\.freeze\(\{ packageName: "[^"]+", executableDigest: "([a-f0-9]+)", size: (\d+)/g)];
expect(records).toHaveLength(3);
for (const [, platform, sha256, size] of records) {
const digest = createHash("sha256").update(JSON.stringify([{ path: "copilot", sha256, size: Number(size), executable: true }])).digest("hex");
expect(COPILOT_CLOSURE_SHA256[platform as keyof typeof COPILOT_CLOSURE_SHA256]).toBe(digest);
}
});
it("ignores ambient executable selection and supplies only pinned native launch inputs", async () => {
const openCommand = vi.fn();
vi.mocked(verifyNativeAcpxInstallation).mockResolvedValueOnce({ commandDigest: "sha256:closure", agentServerPackageJsonPath: "/verified/closure.json", agentRuntimePackageJsonPath: null, openCommand });
const before = process.env.COPILOT_PATH;
process.env.COPILOT_PATH = "/untrusted/copilot";
try {
const result = await verifyCopilotInstallation(QUALIFIED_ACPX_PROFILES.copilot);
const root = fileURLToPath(new URL(`../../../provider-assets/copilot/${process.platform}-${process.arch}`, import.meta.url));
expect(verifyNativeAcpxInstallation).toHaveBeenCalledWith({
distributionRoot: root, manifestPath: `${root}/.paperclip-copilot-closure.json`,
expectedClosureSha256: COPILOT_CLOSURE_SHA256[`${process.platform}-${process.arch}` as keyof typeof COPILOT_CLOSURE_SHA256],
executable: "copilot", fixedArguments: COPILOT_LAUNCH_ARGUMENTS, isolatedCacheEnvironmentName: "COPILOT_PKG_CACHE_HOME",
});
expect(result.commandDigest).toBe(QUALIFIED_ACPX_PROFILES.copilot.commandDigest);
expect(result.openCommand).toBe(openCommand);
} finally { if (before === undefined) delete process.env.COPILOT_PATH; else process.env.COPILOT_PATH = before; }
});
it("rejects changed profile identities before native file access", async () => {
vi.mocked(verifyNativeAcpxInstallation).mockClear();
for (const override of [{ agentServerVersion: "latest" }, { commandDigest: "sha256:untrusted" }, { agent: "cursor" }, { agentProfileVersion: 1 }]) {
await expect(verifyCopilotInstallation({ ...QUALIFIED_ACPX_PROFILES.copilot, ...override } as never)).rejects.toThrow("exact pinned");
}
expect(verifyNativeAcpxInstallation).not.toHaveBeenCalled();
});
});
@@ -0,0 +1,54 @@
import { basename, dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { verifyNativeAcpxInstallation, type VerifiedAcpxInstallation } from "./installation-integrity.js";
import { COPILOT_LAUNCH_ARGUMENTS, COPILOT_VERSION } from "./copilot-profile.js";
import { QUALIFIED_ACPX_PROFILES, type QualifiedAcpxProfile } from "./qualified-profiles.js";
export const COPILOT_CLOSURE_SHA256 = Object.freeze({
"darwin-arm64": "fb3b367a45cd76122fe931521fa2a18adf234ba944fc302db9e10e005e57037e",
"darwin-x64": "05f3497b336b3efdec347beb2e3b80b02cfa95f811fafddc25d0b029ab95d711",
"linux-x64": "1a675c5b54ae4d94f08718a318451e0499708ded388b4cfd98acec6b4311ccbd",
});
/** Admission primitive only. Pending candidates remain gated by qualification. */
export async function verifyCopilotInstallation(profile: QualifiedAcpxProfile): Promise<VerifiedAcpxInstallation> {
const expected = QUALIFIED_ACPX_PROFILES.copilot;
if (profile.agent !== "copilot" || profile.agentProfileVersion !== 2
|| profile.acpxVersion !== expected.acpxVersion || profile.agentServerPackage !== "@github/copilot"
|| profile.agentServerVersion !== COPILOT_VERSION || profile.commandDigest !== expected.commandDigest
|| profile.agentRuntimePackage !== null || profile.agentRuntimeVersion !== null) {
throw new Error("Copilot installation requires the exact pinned candidate profile");
}
const platform = `${process.platform}-${process.arch}`;
if (!Object.prototype.hasOwnProperty.call(COPILOT_CLOSURE_SHA256, platform)) {
throw new Error(`Copilot native distribution is not pinned for ${platform}`);
}
const distributionRoot = join(ownedPackageRoot(), "provider-assets", "copilot", platform);
const native = await verifyNativeAcpxInstallation({
distributionRoot,
manifestPath: join(distributionRoot, ".paperclip-copilot-closure.json"),
expectedClosureSha256: COPILOT_CLOSURE_SHA256[platform as keyof typeof COPILOT_CLOSURE_SHA256],
executable: "copilot", fixedArguments: COPILOT_LAUNCH_ARGUMENTS,
isolatedCacheEnvironmentName: "COPILOT_PKG_CACHE_HOME",
});
// The host identity binds the versioned profile. Native admission independently
// binds the complete platform closure before creating each single-use lease.
return Object.freeze({ ...native, commandDigest: expected.commandDigest });
}
function ownedPackageRoot(): string {
const modulePath = fileURLToPath(import.meta.url);
const directory = dirname(modulePath);
if (basename(directory) === "acpx" && basename(dirname(directory)) === "drivers"
&& ["src", "dist"].includes(basename(dirname(dirname(directory))))
&& /^copilot-installation\.(?:ts|js)$/.test(basename(modulePath))) {
return resolve(directory, "../../..");
}
// The verified CommonJS entrypoint replaces import.meta.url with its own URL;
// the bundled ESM entrypoint has the same runtime layout.
if (basename(directory) === "cli" && basename(dirname(directory)) === "dist"
&& /^acpx-runtime-sidecar\.(?:cjs|js)$/.test(basename(modulePath))) {
return resolve(directory, "../..");
}
throw new Error("Copilot assets require the runner's build-owned module layout");
}