From fba0ab8162f65304d473c13c8991cfb41c050a9d Mon Sep 17 00:00:00 2001 From: Nicky Leach Date: Fri, 30 Oct 2026 07:42:10 -0700 Subject: [PATCH] fix(runner): preserve GitHub helpers and Codex probe policy (#18212) --- doc/plugins/PLUGIN_RUNNER_EXECUTION.md | 24 +++++ doc/runner-launch-preparation.md | 8 ++ .../execution-environments.md | 4 + maps/feature-map/execution-environments.md | 4 + .../adapter-utils/src/github-launcher.test.ts | 25 +++++- packages/adapter-utils/src/github-launcher.ts | 7 ++ packages/paperclip-runner/acpx-profiles.json | 12 +-- .../runner-core/src/acpx_provider_backend.rs | 3 +- .../runner-core/src/acpx_sidecar_transport.rs | 45 ++++++++++ .../runner-core/src/configured_environment.rs | 5 ++ .../src/generated_acpx_profiles.rs | 6 +- .../tests/native_provider_backend.rs | 2 +- .../scripts/copilot-profile-identity.mjs | 4 +- .../scripts/grok-profile-identity.mjs | 4 +- .../src/configured-environment.ts | 2 +- .../drivers/acpx/copilot-installation.test.ts | 15 +++- .../src/drivers/acpx/environment.test.ts | 22 +++++ .../src/drivers/acpx/environment.ts | 3 + .../src/drivers/acpx/generated-profiles.ts | 12 +-- .../src/drivers/acpx/pi-installation.test.ts | 38 ++++---- .../src/drivers/acpx/profile-compatibility.ts | 6 +- packages/paperclip-runner/src/index.ts | 2 +- .../src/live/runner-provider-launch.test.ts | 35 ++++++++ .../src/live/runner-provider-launch.ts | 32 ++++--- .../src/providers/grok/launcher.cjs | 2 +- .../pi-acp/profile-v36-identity.json | 52 +++++++++++ .../copilot-profile-v89-identity.json | 88 ++++++++++++++++++ .../copilot-profile-v90-identity.json | 88 ++++++++++++++++++ .../copilot-profile-v91-identity.json | 89 +++++++++++++++++++ .../fixtures/grok-profile-v16-identity.json | 35 ++++++++ .../fixtures/grok-profile-v17-identity.json | 35 ++++++++ .../fixtures/grok-profile-v18-identity.json | 35 ++++++++ .../fixtures/grok-profile-v19-identity.json | 36 ++++++++ packages/plugins/sdk/src/environment-tasks.ts | 1 + .../sdk/tests/environment-tasks.test.ts | 12 +++ packages/shared/src/validators/issue.test.ts | 7 ++ packages/shared/src/validators/issue.ts | 1 + scripts/pi-public-install-probe.mjs | 4 +- .../__tests__/heartbeat-project-env.test.ts | 13 +++ .../src/__tests__/workspace-runtime.test.ts | 2 +- .../src/services/heartbeat/run-preparation.ts | 1 + 41 files changed, 763 insertions(+), 58 deletions(-) create mode 100644 packages/paperclip-runner/test-fixtures/pi-acp/profile-v36-identity.json create mode 100644 packages/paperclip-runner/test/fixtures/copilot-profile-v89-identity.json create mode 100644 packages/paperclip-runner/test/fixtures/copilot-profile-v90-identity.json create mode 100644 packages/paperclip-runner/test/fixtures/copilot-profile-v91-identity.json create mode 100644 packages/paperclip-runner/test/fixtures/grok-profile-v16-identity.json create mode 100644 packages/paperclip-runner/test/fixtures/grok-profile-v17-identity.json create mode 100644 packages/paperclip-runner/test/fixtures/grok-profile-v18-identity.json create mode 100644 packages/paperclip-runner/test/fixtures/grok-profile-v19-identity.json diff --git a/doc/plugins/PLUGIN_RUNNER_EXECUTION.md b/doc/plugins/PLUGIN_RUNNER_EXECUTION.md index f9304a854ad..117ccca2166 100644 --- a/doc/plugins/PLUGIN_RUNNER_EXECUTION.md +++ b/doc/plugins/PLUGIN_RUNNER_EXECUTION.md @@ -268,3 +268,27 @@ credential helpers, or command proxies can implement the same semantics. Deploy the updated Core and plugin together. A configured GitHub connection on an older task provider returns an explicit upgrade error. Unconfigured providers continue ordinary work without authenticated GitHub access. + +Managed GitHub command providers must retain the qualified provider executable +and arguments. Supply helper discovery through the task environment instead of +wrapping that executable. Claude ACPX preserves explicit `BASH_ENV`, `ZDOTDIR`, +`GH_CONFIG_DIR`, and `PAPERCLIP_GITHUB_LAUNCHER_DIR` selectors across the sidecar +boundary. It does not forward GitHub tokens or broker capabilities to the provider. +The helper reads the current task capability at invocation, including after warm +reuse. These selectors must come from the trusted environment owner. +Reference helpers mark only their child operation and reject re-entry before resolving +another executable or capturing credentials. A PATH shim that calls a helper's +absolute path fails with `launcher_recursion_rejected` (exit 126), preventing +recursive process growth. Offline Git fixtures must resolve the native binary +outside managed helper paths and exclude runtime shell startup hooks. + + +The pure launch factory exports `RUNNER_GITHUB_LAUNCHER_VERSION = 1` for this +contract. A provider must check it as well as the host broker schema and its +Launcher capability before advertising managed GitHub commands. The installed +Runner must match the factory's source revision. + +The helper projection is marked by the controller-owned +`PAPERCLIP_GITHUB_LAUNCHER_DIR`. Task overrides cannot supply that marker or the +shell selectors. Run preparation strips stored bindings before secret resolution, +and configured task-environment projections reject the shell selectors too. diff --git a/doc/runner-launch-preparation.md b/doc/runner-launch-preparation.md index 3252e4f2643..ccd2f172f37 100644 --- a/doc/runner-launch-preparation.md +++ b/doc/runner-launch-preparation.md @@ -32,6 +32,14 @@ descriptor. The transport checks Core's selected profile and policy before registration and launch. Core still supplies authorized tools and completion criteria through PRP. A failed preparation does not enable a fallback launch. +### Codex probe policy + +Core can select `settings.disableCodexProjectInstructions` for a hello probe. +Preparation must preserve it as `project_doc_max_bytes=0` before `app-server`, +after any environment-supplied overrides. Core validates this setting alongside +`disableCodexWebSearch`. Ordinary tasks retain project instructions when the +setting is absent or false. This does not change the selected filesystem policy. + ### Working directory In this mode the environment chooses the provider working directory. Core does diff --git a/maps/agent-access-map/execution-environments.md b/maps/agent-access-map/execution-environments.md index ba1bd355a87..2211a4c4b1b 100644 --- a/maps/agent-access-map/execution-environments.md +++ b/maps/agent-access-map/execution-environments.md @@ -192,6 +192,10 @@ The [focused policy tests](../../server/src/__tests__/environment-task-runtime.t cover these checks; this is not live provider verification. This source update does not qualify live agent access, warm retention, or a provider. See the [execution contract](../../doc/plugins/PLUGIN_RUNNER_EXECUTION.md). +Managed GitHub shell selectors are controller-owned. Task overrides cannot set +BASH_ENV, ZDOTDIR, GH_CONFIG_DIR or the launcher-directory marker. Stored bindings +are stripped before secret resolution, and configured environment projections +reject shell selectors. The native qualified executable checks still apply. Same-run checkpoint recovery also uses the saved lease path after a turn omits its completion result. It requires verified prior termination, retained session diff --git a/maps/feature-map/execution-environments.md b/maps/feature-map/execution-environments.md index 964cb2a2c35..d0213afe334 100644 --- a/maps/feature-map/execution-environments.md +++ b/maps/feature-map/execution-environments.md @@ -142,6 +142,10 @@ uses the real Runner and a synthetic Codex provider through loopback PRP; TLS termination is replaced by the fixture. It does not qualify a live plugin provider. The plugin prepares execution-host resources and the provider descriptor. Core validates the result, then supplies task identity and PRP launch limits. +For managed GitHub commands, verify the qualified executable remains canonical, +controller-staged helper discovery survives task entry, and a task-supplied shell +selector is rejected. A launch description or a healthy Runner alone is not proof +of authenticated git/gh execution. ### Storybook verification diff --git a/packages/adapter-utils/src/github-launcher.test.ts b/packages/adapter-utils/src/github-launcher.test.ts index cb64af9a158..85e25d63094 100644 --- a/packages/adapter-utils/src/github-launcher.test.ts +++ b/packages/adapter-utils/src/github-launcher.test.ts @@ -1,6 +1,6 @@ import { execFile } from "node:child_process"; import { createServer } from "node:http"; -import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises"; +import { mkdtemp, mkdir, readFile, writeFile, rm } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { promisify } from "node:util"; @@ -11,6 +11,29 @@ const cleanups: Array<() => Promise> = []; afterEach(async () => { for (const cleanup of cleanups.splice(0).reverse()) await cleanup(); }); describe("managed GitHub launchers", () => { + it.each([false, true])("rejects a PATH shim that re-enters the same helper before it can recurse (broker file: %s)", async brokerFile => { + const root = await mkdtemp(path.join(os.tmpdir(), "paperclip-github-recursion-")); + cleanups.push(() => rm(root, { recursive: true, force: true })); + const managed = path.join(root, "managed"), shim = path.join(root, "shim"), calls = path.join(root, "calls"); + await mkdir(managed); await mkdir(shim); + const launcher = path.join(managed, "git"); + await writeFile(launcher, githubLauncherSource({ brokerFile }), { mode: 0o700 }); + // Bound the pre-fix reproduction: the fixture stops at three invocations. + await writeFile(path.join(shim, "git"), `#!${process.execPath} +const fs = require("node:fs"), {spawnSync} = require("node:child_process"); +fs.appendFileSync(${JSON.stringify(calls)}, "call\\n"); +if (fs.readFileSync(${JSON.stringify(calls)}, "utf8").trim().split("\\n").length >= 3) process.exit(99); +const result = spawnSync(${JSON.stringify(launcher)}, process.argv.slice(2), {env: process.env, stdio: "inherit"}); +process.exit(result.status ?? 98); +`, { mode: 0o700 }); + const env = { ...process.env, ...githubBrokerEnvironment({}, {url: "", token: ""}), + PATH: `${managed}${path.delimiter}${shim}${path.delimiter}${path.dirname(process.execPath)}${path.delimiter}/usr/bin${path.delimiter}/bin` }; + await expect(exec(launcher, ["--version"], {env, timeout: 5000})).rejects.toMatchObject({ + code: 126, stderr: expect.stringContaining("launcher_recursion_rejected"), + }); + expect((await readFile(calls, "utf8")).trim().split("\n")).toEqual(["call"]); + }); + it("snapshots a staged tree and reports unstaged files without capturing GitHub credentials", async () => { const root = await mkdtemp(path.join(os.tmpdir(), "paperclip-github-snapshot-")); cleanups.push(() => rm(root, { recursive: true, force: true })); diff --git a/packages/adapter-utils/src/github-launcher.ts b/packages/adapter-utils/src/github-launcher.ts index c9bbd4d1e18..ea38405306d 100644 --- a/packages/adapter-utils/src/github-launcher.ts +++ b/packages/adapter-utils/src/github-launcher.ts @@ -5,6 +5,12 @@ const fs = require('node:fs'); const path = require('node:path'); const os = require('node:os'); const { spawn } = require('node:child_process'); +// A PATH shim can capture this launcher's absolute path and call it again. +// Reject that cycle before resolving another executable or capturing authority. +if (process.env.PAPERCLIP_GITHUB_OPERATION_ACTIVE) { + process.stderr.write('Paperclip: GitHub launcher_recursion_rejected.\n'); + process.exit(126); +} const directory = path.dirname(fs.realpathSync(process.argv[1])); const program = path.basename(process.argv[1]); const originalPath = (process.env.PATH || '').split(path.delimiter).filter(p => { @@ -129,6 +135,7 @@ ${options.brokerFile ? String.raw` } catch { diagnostic('broker_transport_unavailable'); } } // Only this invocation and its children inherit the captured credential. + env.PAPERCLIP_GITHUB_OPERATION_ACTIVE = '1'; // Its Git children use the real binary, so steering cannot split a gh operation. env.PATH = originalPath.join(path.delimiter); // Nested shell aliases must not reload the parent launcher profile and diff --git a/packages/paperclip-runner/acpx-profiles.json b/packages/paperclip-runner/acpx-profiles.json index 586d494328e..603534a7706 100644 --- a/packages/paperclip-runner/acpx-profiles.json +++ b/packages/paperclip-runner/acpx-profiles.json @@ -15,21 +15,21 @@ "qualificationStatus": "pending" }, "grok": { - "agentProfileVersion": 15, + "agentProfileVersion": 19, "agentServerPackage": "builtin:grok-acp", "agentServerVersion": "1", "agentRuntimePackage": "native:grok", "agentRuntimeVersion": "1.0.13", - "commandDigest": "sha256:658e6d41ce6fc7080aa6655d7a97121b3cae6214f2d4c074284d958b95bbe769", + "commandDigest": "sha256:5205c2197b94cfbd3e62fbeea027f2e80d092f736ca52656dda2f7044ba9a5ca", "requiresProviderPolicy": false }, "pi": { - "agentProfileVersion": 35, + "agentProfileVersion": 36, "agentServerPackage": "pi-acp", "agentServerVersion": "0.0.33", "agentRuntimePackage": "@earendil-works/pi-coding-agent", "agentRuntimeVersion": "1.0.0", - "commandDigest": "sha256:9473de6ef4baf2bd82fe8b6757239bdc160960b0a78ae6f47fa9e0996544226c", + "commandDigest": "sha256:faafb66c09c1efc7eb4eda6baaa5813c975e8a992f9cd333687695aa5522d7dc", "requiresProviderPolicy": true }, "cursor": { @@ -42,12 +42,12 @@ "requiresProviderPolicy": true }, "copilot": { - "agentProfileVersion": 88, + "agentProfileVersion": 91, "agentServerPackage": "@github/copilot", "agentServerVersion": "1.0.88", "agentRuntimePackage": null, "agentRuntimeVersion": null, - "commandDigest": "sha256:b9c13b83628be07086a88394ae671997291b77bbdbf45b0bd1c4e91b7121bc9d", + "commandDigest": "sha256:e4383c48ad21898dfe38609fdc8c0fa9a76dfcb8582a64b5415f107846904aa9", "requiresProviderPolicy": true }, "claude": { diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs index 7f3a2c4897a..d9c9a8d380d 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs @@ -3351,7 +3351,7 @@ mod tests { #[test] fn pi_profile_matches_published_identity_and_rejects_prior_profiles() { let published: Value = serde_json::from_str(include_str!( - "../../../../test-fixtures/pi-acp/profile-v35-identity.json" + "../../../../test-fixtures/pi-acp/profile-v36-identity.json" )) .unwrap(); let mut value = descriptor("codex"); @@ -3368,6 +3368,7 @@ mod tests { current.validate(&context()).unwrap(); for prior in [ + include_str!("../../../../test-fixtures/pi-acp/profile-v35-identity.json"), include_str!("../../../../test-fixtures/pi-acp/profile-v34-identity.json"), include_str!("../../../../test-fixtures/pi-acp/profile-v33-identity.json"), include_str!("../../../../test-fixtures/pi-acp/profile-v32-identity.json"), diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs index 73bcdf9459e..c57dd9a1ebd 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs @@ -261,6 +261,11 @@ impl AcpxSidecarTransport { } if agent == "claude" { keys.extend_from_slice(&[ + // Launcher/controller-owned helper paths; credentials stay behind the broker. + "BASH_ENV", + "ZDOTDIR", + "GH_CONFIG_DIR", + "PAPERCLIP_GITHUB_LAUNCHER_DIR", "ANTHROPIC_BASE_URL", "CLAUDE_CODE_ENABLE_TELEMETRY", "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA", @@ -1069,6 +1074,46 @@ mod tests { use super::*; + #[test] + #[cfg(unix)] + fn managed_claude_helpers_reach_the_sidecar_without_host_credentials() { + const CHILD: &str = "PAPERCLIP_TEST_MANAGED_HELPER_CHILD"; + if std::env::var_os(CHILD).is_none() { + let output = std::process::Command::new(std::env::current_exe().unwrap()) + .args(["--exact", "acpx_sidecar_transport::tests::managed_claude_helpers_reach_the_sidecar_without_host_credentials"]) + .env_clear() + .env(CHILD, "1") + .env("PATH", "/usr/bin:/bin") + .env("BASH_ENV", "/managed/.bashrc") + .env("ZDOTDIR", "/managed") + .env("GH_CONFIG_DIR", "/managed") + .env("PAPERCLIP_GITHUB_LAUNCHER_DIR", "/managed") + .env("GH_TOKEN", "unbound-secret") + .env("PAPERCLIP_GITHUB_BROKER_TOKEN", "unbound-capability") + .output() + .unwrap(); + assert!( + output.status.success(), + "{}", + String::from_utf8_lossy(&output.stdout) + ); + return; + } + let config = AcpxSidecarTransportConfig { + command: PathBuf::from("/bin/sh"), + args: vec!["-c".into(), "printf '%s|%s|%s|%s|%s|%s\\n' \"$BASH_ENV\" \"$ZDOTDIR\" \"$GH_CONFIG_DIR\" \"$PAPERCLIP_GITHUB_LAUNCHER_DIR\" \"$GH_TOKEN\" \"$PAPERCLIP_GITHUB_BROKER_TOKEN\"".into()], + verified_launch: None, + request_timeout: Duration::from_secs(1), + shutdown_grace: Duration::from_millis(100), + }; + let mut transport = AcpxSidecarTransport::start_for_agent(&config, "claude").unwrap(); + let line = transport + .receive_stdout_line(Duration::from_secs(2), "managed-helper test") + .unwrap() + .expect("sidecar did not report its environment"); + assert_eq!(line, "/managed/.bashrc|/managed|/managed|/managed||"); + } + #[test] fn parse_frame_does_not_echo_untrusted_deserialization_details() { let cases = [ diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/configured_environment.rs b/packages/paperclip-runner/runner/crates/runner-core/src/configured_environment.rs index 3f25b976516..f9d3cecf9c5 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/configured_environment.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/configured_environment.rs @@ -38,6 +38,8 @@ const RESERVED: &[&str] = &[ "ANTHROPIC_AUTH_TOKEN", "AWS_BEARER_TOKEN_BEDROCK", "BASH_ENV", + "ZDOTDIR", + "GH_CONFIG_DIR", "CLAUDE_CODE_OAUTH_TOKEN", "CODEX_API_KEY", "CODEX_HOME", @@ -199,6 +201,9 @@ mod tests { "not-json", "[1]", "[\"NODE_OPTIONS\"]", + "[\"BASH_ENV\"]", + "[\"ZDOTDIR\"]", + "[\"GH_CONFIG_DIR\"]", "[\"PAPERCLIP_API_KEY\"]", "[\"PAPERCLIP_RUNNER_BOOTSTRAP_TICKET\"]", "[\"PAPERCLIP_RUNTIME_NOTIFICATION_CONTEXT\"]", diff --git a/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs b/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs index 1fed7fa297c..48244cd0f51 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/src/generated_acpx_profiles.rs @@ -31,7 +31,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option { agent_runtime_package: Some("native:grok"), agent_runtime_version: Some("1.0.13"), command_digest: - "sha256:658e6d41ce6fc7080aa6655d7a97121b3cae6214f2d4c074284d958b95bbe769", + "sha256:5205c2197b94cfbd3e62fbeea027f2e80d092f736ca52656dda2f7044ba9a5ca", requires_provider_policy: false, qualification_pending: false, }, @@ -41,7 +41,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option { agent_runtime_package: Some("@earendil-works/pi-coding-agent"), agent_runtime_version: Some("1.0.0"), command_digest: - "sha256:9473de6ef4baf2bd82fe8b6757239bdc160960b0a78ae6f47fa9e0996544226c", + "sha256:faafb66c09c1efc7eb4eda6baaa5813c975e8a992f9cd333687695aa5522d7dc", requires_provider_policy: true, qualification_pending: false, }, @@ -61,7 +61,7 @@ pub(crate) fn acpx_release_profile(agent: &str) -> Option { agent_runtime_package: None, agent_runtime_version: None, command_digest: - "sha256:b9c13b83628be07086a88394ae671997291b77bbdbf45b0bd1c4e91b7121bc9d", + "sha256:e4383c48ad21898dfe38609fdc8c0fa9a76dfcb8582a64b5415f107846904aa9", requires_provider_policy: true, qualification_pending: false, }, diff --git a/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs b/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs index 71395ec5e72..656f57284ec 100644 --- a/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs +++ b/packages/paperclip-runner/runner/crates/runner-core/tests/native_provider_backend.rs @@ -17,7 +17,7 @@ use sha2::{Digest, Sha256}; const CODEX_ACPX_DIGEST: &str = "sha256:443491f4e814501a5dc47347e4510e84faeec9c9382e2d4e53bfe69386d5aabc"; const PI_ACPX_DIGEST: &str = - "sha256:9473de6ef4baf2bd82fe8b6757239bdc160960b0a78ae6f47fa9e0996544226c"; + "sha256:faafb66c09c1efc7eb4eda6baaa5813c975e8a992f9cd333687695aa5522d7dc"; fn temporary_directory(label: &str) -> PathBuf { let nonce = SystemTime::now() diff --git a/packages/paperclip-runner/scripts/copilot-profile-identity.mjs b/packages/paperclip-runner/scripts/copilot-profile-identity.mjs index 48573f6cb45..fae0caaaf1c 100644 --- a/packages/paperclip-runner/scripts/copilot-profile-identity.mjs +++ b/packages/paperclip-runner/scripts/copilot-profile-identity.mjs @@ -7,9 +7,9 @@ import { fileURLToPath } from "node:url"; import { resolve, dirname } from "node:path"; const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const fixture = resolve(root, "test/fixtures/copilot-profile-v88-identity.json"); +const fixture = resolve(root, "test/fixtures/copilot-profile-v91-identity.json"); const historical = JSON.parse(readFileSync(resolve(root, "test/fixtures/copilot-profile-v12-identity.json"), "utf8")); -const declaration = { ...historical.declaration, agentProfileVersion: 88 }; +const declaration = { ...historical.declaration, agentProfileVersion: 91 }; delete declaration.permissionIdentitySourceSha256; const sources = { displayEventSourceSha256: "src/drivers/acpx/copilot-events.ts", diff --git a/packages/paperclip-runner/scripts/grok-profile-identity.mjs b/packages/paperclip-runner/scripts/grok-profile-identity.mjs index 3dea2f15e96..bf7764de88c 100644 --- a/packages/paperclip-runner/scripts/grok-profile-identity.mjs +++ b/packages/paperclip-runner/scripts/grok-profile-identity.mjs @@ -7,9 +7,9 @@ import { fileURLToPath } from "node:url"; const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const repositoryRoot = resolve(packageRoot, "../.."); -// Versions 1–14 remain decodable historical identities. Versioned fixtures are +// Versions 1–18 remain decodable historical identities. Versioned fixtures are // immutable; advance this version before changing a versioned declaration. -export const GROK_PROFILE_VERSION = 15; +export const GROK_PROFILE_VERSION = 19; // Contract surfaces: ACPX wire patch; sidecar parsing and error translation; // native effort/search/credential admission; host/sandbox/tool-bridge execution; // launcher integrity and platform pins; Rust descriptor writes, session state, diff --git a/packages/paperclip-runner/src/configured-environment.ts b/packages/paperclip-runner/src/configured-environment.ts index 487e375cdaf..93e1f8a5d5e 100644 --- a/packages/paperclip-runner/src/configured-environment.ts +++ b/packages/paperclip-runner/src/configured-environment.ts @@ -27,7 +27,7 @@ export const GENERATED_RUNTIME_ENVIRONMENT_KEYS: ReadonlySet = new Set([ const RESERVED_KEYS = new Set([ "PATH", "HOME", "USERPROFILE", "CODEX_HOME", "AGENT_HOME", "SHELL", "NODE_OPTIONS", "NODE_PATH", - "PYTHONPATH", "PYTHONHOME", "RUBYOPT", "RUBYLIB", "PERL5OPT", "PERL5LIB", "BASH_ENV", "ENV", + "PYTHONPATH", "PYTHONHOME", "RUBYOPT", "RUBYLIB", "PERL5OPT", "PERL5LIB", "BASH_ENV", "ENV", "ZDOTDIR", "GH_CONFIG_DIR", "OPENAI_API_KEY", "CODEX_API_KEY", "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "CLAUDE_CODE_OAUTH_TOKEN", "AWS_BEARER_TOKEN_BEDROCK", "OPENROUTER_API_KEY", "XAI_API_KEY", "CURSOR_API_KEY", "CURSOR_AUTH_TOKEN", "COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN", "PAPERCLIP_GIT_TOKEN", diff --git a/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts b/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts index c10f8a76d2c..54ec8c4d85e 100644 --- a/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/copilot-installation.test.ts @@ -86,10 +86,21 @@ async function withCopilotTestPlatform(arch: "x64" | "arm64", run: () => Prom vi.mock("./installation-integrity.js", () => ({ verifyNativeAcpxInstallation: vi.fn() })); describe("Copilot build-owned installation", () => { + it("preserves published v88 while binding managed launch policy in v91", async () => { + const bytes = readFileSync(new URL("../../../test/fixtures/copilot-profile-v88-identity.json", import.meta.url)); + expect(createHash("sha256").update(bytes).digest("hex")).toBe("81e5fbccf057c04e012f638dc7a02c5758bf6ca67f7c4ed0045f3f61525401c2"); + const prior = JSON.parse(bytes.toString("utf8")); + const current = JSON.parse(readFileSync(new URL("../../../test/fixtures/copilot-profile-v91-identity.json", import.meta.url), "utf8")); + expect(Object.keys(current.declaration).filter(key => current.declaration[key] !== prior.declaration[key]).sort()).toEqual(["agentProfileVersion", "configuredEnvironmentRustSourceSha256", "configuredEnvironmentSourceSha256", "credentialBindingSourceSha256", "profileCompatibilitySourceSha256", "providerLaunchSourceSha256"]); + expect(isSupportedAcpxProfileVersion("copilot", 88)).toBe(true); + await expect(verifyCopilotInstallation({ ...QUALIFIED_ACPX_PROFILES.copilot, + agentProfileVersion: 88, commandDigest: prior.commandDigest })).rejects.toThrow("exact pinned"); + }); + it("admits the current profile with environment launch preparation", () => { - expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(88); + expect(QUALIFIED_ACPX_PROFILES.copilot.agentProfileVersion).toBe(91); expect(QUALIFIED_ACPX_PROFILES.copilot.qualificationStatus).toBeUndefined(); - expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:b9c13b83628be07086a88394ae671997291b77bbdbf45b0bd1c4e91b7121bc9d"); + expect(QUALIFIED_ACPX_PROFILES.copilot.commandDigest).toBe("sha256:e4383c48ad21898dfe38609fdc8c0fa9a76dfcb8582a64b5415f107846904aa9"); }); it("preserves published v65 while binding retained-maintenance waits in v66", async () => { diff --git a/packages/paperclip-runner/src/drivers/acpx/environment.test.ts b/packages/paperclip-runner/src/drivers/acpx/environment.test.ts index 42d19992879..c744b5d99b7 100644 --- a/packages/paperclip-runner/src/drivers/acpx/environment.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/environment.test.ts @@ -6,6 +6,28 @@ afterEach(() => vi.unstubAllEnvs()); import { ACPX_CREDENTIAL_BINDING_ENV, createAcpxCredentialBinding, createAcpxSidecarHostEnvironment, createSanitizedAcpxSpawnInput } from "./environment.js"; describe("ACPX launch environment", () => { + it("rejects task shell selectors without a controller-owned helper projection", () => { + const selectors = { BASH_ENV: "/workspace/hook.sh", ZDOTDIR: "/workspace", GH_CONFIG_DIR: "/workspace/gh" }; + expect(createSanitizedAcpxSpawnInput(selectors, "claude").env).toEqual({}); + for (const [key, value] of Object.entries(selectors)) { + expect(configuredEnvironmentProjection({[key]: value})).not.toHaveProperty(key); + expect(() => createSanitizedAcpxSpawnInput({[key]:value, [CONFIGURED_ENVIRONMENT_KEYS]:JSON.stringify([key])}, "claude")).toThrow("configured environment"); + } + }); + it("keeps explicit Claude managed-command selectors without forwarding GitHub credentials", () => { + const helpers = { PATH: "/managed:/usr/bin", BASH_ENV: "/managed/.bashrc", ZDOTDIR: "/managed", + GH_CONFIG_DIR: "/managed", PAPERCLIP_GITHUB_LAUNCHER_DIR: "/managed" }; + const source = { ...helpers, GH_TOKEN: "host-secret", GITHUB_TOKEN: "host-secret", + PAPERCLIP_GITHUB_BROKER_TOKEN: "host-capability", NODE_OPTIONS: "--require=untrusted" }; + expect(createSanitizedAcpxSpawnInput(source, "claude").env).toEqual(helpers); + for (const [key, value] of Object.entries(helpers)) vi.stubEnv(key, value); + const ambient = createSanitizedAcpxSpawnInput(undefined, "claude").env; + const sibling = createSanitizedAcpxSpawnInput(source, "cursor").env; + for (const key of Object.keys(helpers).filter(key => key !== "PATH")) { + expect(ambient).not.toHaveProperty(key); + expect(sibling).not.toHaveProperty(key); + } + }); it("does not inherit ambient prepared storage", () => { vi.stubEnv("PAPERCLIP_RUNNER_PREPARED_HARNESS_HOME", "/ambient/claude"); vi.stubEnv("CLAUDE_CONFIG_DIR", "/ambient/claude"); diff --git a/packages/paperclip-runner/src/drivers/acpx/environment.ts b/packages/paperclip-runner/src/drivers/acpx/environment.ts index 40eb872e16e..59cfd0dea07 100644 --- a/packages/paperclip-runner/src/drivers/acpx/environment.ts +++ b/packages/paperclip-runner/src/drivers/acpx/environment.ts @@ -138,7 +138,10 @@ export function createSanitizedAcpxSpawnInput( ...(environment === undefined ? [] : ["PAPERCLIP_AGENT_KEY_ID", "PAPERCLIP_AGENT_PUBLIC_KEY", "PAPERCLIP_AGENT_PRIVATE_KEY"]), ...credentialNames, ...(agent === "claude" ? CLAUDE_ROUTING_ENV_KEYS : []), + // The environment owner supplies helper paths, not a replacement provider executable. ...(agent === "claude" && environment !== undefined ? ["MCP_TOOL_TIMEOUT"] : []), + ...(agent === "claude" && environment?.PAPERCLIP_GITHUB_LAUNCHER_DIR?.trim() + ? ["BASH_ENV", "ZDOTDIR", "GH_CONFIG_DIR", "PAPERCLIP_GITHUB_LAUNCHER_DIR"] : []), ...(usesPreconfiguredHome(source) ? [...PRECONFIGURED_HOME_KEYS, "PAPERCLIP_RUNNER_HOME_MODE", "ACPX_CLAUDE_INCLUDE_USER_SETTINGS"] : []), ...(agent === "claude" && environment?.PAPERCLIP_RUNNER_PREPARED_HARNESS_HOME ? [...PRECONFIGURED_HOME_KEYS, "PAPERCLIP_RUNNER_PREPARED_HARNESS_HOME"] : []), diff --git a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts index cbd2e3ae2c1..02fae2b2212 100644 --- a/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts +++ b/packages/paperclip-runner/src/drivers/acpx/generated-profiles.ts @@ -23,12 +23,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = { "protocolVersion": 1, "acpxVersion": "0.13.1", "agent": "grok", - "agentProfileVersion": 15, + "agentProfileVersion": 19, "agentServerPackage": "builtin:grok-acp", "agentServerVersion": "1", "agentRuntimePackage": "native:grok", "agentRuntimeVersion": "1.0.13", - "commandDigest": "sha256:658e6d41ce6fc7080aa6655d7a97121b3cae6214f2d4c074284d958b95bbe769", + "commandDigest": "sha256:5205c2197b94cfbd3e62fbeea027f2e80d092f736ca52656dda2f7044ba9a5ca", "permissionPolicy": "interactive" }, "pi": { @@ -36,12 +36,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = { "protocolVersion": 1, "acpxVersion": "0.13.1", "agent": "pi", - "agentProfileVersion": 35, + "agentProfileVersion": 36, "agentServerPackage": "pi-acp", "agentServerVersion": "0.0.33", "agentRuntimePackage": "@earendil-works/pi-coding-agent", "agentRuntimeVersion": "1.0.0", - "commandDigest": "sha256:9473de6ef4baf2bd82fe8b6757239bdc160960b0a78ae6f47fa9e0996544226c", + "commandDigest": "sha256:faafb66c09c1efc7eb4eda6baaa5813c975e8a992f9cd333687695aa5522d7dc", "permissionPolicy": "interactive" }, "cursor": { @@ -62,12 +62,12 @@ export const QUALIFIED_ACPX_PROFILE_DATA = { "protocolVersion": 1, "acpxVersion": "0.13.1", "agent": "copilot", - "agentProfileVersion": 88, + "agentProfileVersion": 91, "agentServerPackage": "@github/copilot", "agentServerVersion": "1.0.88", "agentRuntimePackage": null, "agentRuntimeVersion": null, - "commandDigest": "sha256:b9c13b83628be07086a88394ae671997291b77bbdbf45b0bd1c4e91b7121bc9d", + "commandDigest": "sha256:e4383c48ad21898dfe38609fdc8c0fa9a76dfcb8582a64b5415f107846904aa9", "permissionPolicy": "interactive" }, "claude": { diff --git a/packages/paperclip-runner/src/drivers/acpx/pi-installation.test.ts b/packages/paperclip-runner/src/drivers/acpx/pi-installation.test.ts index 5704ac6df2b..b712d64a190 100644 --- a/packages/paperclip-runner/src/drivers/acpx/pi-installation.test.ts +++ b/packages/paperclip-runner/src/drivers/acpx/pi-installation.test.ts @@ -41,6 +41,14 @@ async function fixture() { } describe("Pi installation factory", () => { + it("preserves v35 decoding while binding managed command controls in v36", async () => { + const prior = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v35-identity.json", import.meta.url), "utf8")); + const current = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v36-identity.json", import.meta.url), "utf8")); + expect(Object.keys(current.declaration).filter(key => JSON.stringify(current.declaration[key]) !== JSON.stringify(prior.declaration[key])).sort()) + .toEqual(["agentProfileVersion", "credentialEnvironmentSourceSha256"]); + expect(isSupportedAcpxProfileVersion("pi", 35)).toBe(true); + }); + it("preserves profile 13 and changes only the version and corrected ACPX patch binding", async () => { const prior = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v13-identity.json", import.meta.url), "utf8")); const current = JSON.parse(await readFile(new URL("../../../test-fixtures/pi-acp/profile-v14-identity.json", import.meta.url), "utf8")); @@ -218,21 +226,21 @@ describe("Pi installation factory", () => { it("rejects legacy profiles and tampered executable identities", () => { expect(() => assertPiInstallationProfile(candidate())).not.toThrow(); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 1 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 2 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 3 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 4 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 5 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 6 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 7 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 8 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 9 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 10 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 11 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 12 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 13 })).toThrow("version 35"); - expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 14 })).toThrow("version 35"); - for (const priorVersion of [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]) expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: priorVersion })).toThrow("version 35"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 1 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 2 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 3 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 4 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 5 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 6 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 7 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 8 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 9 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 10 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 11 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 12 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 13 })).toThrow("version 36"); + expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: 14 })).toThrow("version 36"); + for (const priorVersion of [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35]) expect(() => assertPiInstallationProfile({ ...candidate(), agentProfileVersion: priorVersion })).toThrow("version 36"); for (const changed of [{ agentServerVersion: "latest" }, { commandDigest: `sha256:${"0".repeat(64)}` }, { agentRuntimePackage: "ambient-pi" }]) { expect(() => assertPiInstallationProfile({ ...candidate(), ...changed })).toThrow("trusted declaration"); } diff --git a/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts b/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts index 2a8d9094ecf..6e08a34aa12 100644 --- a/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts +++ b/packages/paperclip-runner/src/drivers/acpx/profile-compatibility.ts @@ -2,13 +2,13 @@ import { QUALIFIED_ACPX_PROFILE_DATA } from "./generated-profiles.js"; /** Decodable historical profile revisions, not permission to launch them. * Exact current profile/package/digest admission is checked separately. */ -type HistoricalAcpxProfileVersion = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87; +type HistoricalAcpxProfileVersion = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90; type CurrentAcpxProfileVersion = typeof QUALIFIED_ACPX_PROFILE_DATA[keyof typeof QUALIFIED_ACPX_PROFILE_DATA]["agentProfileVersion"]; export type AcpxProfileVersion = HistoricalAcpxProfileVersion | CurrentAcpxProfileVersion; const historicalVersions: Readonly> = { - pi: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34], claude: [1, 2, 3, 4, 5], codex: [1, 2, 3, 4, 5], - grok: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], copilot: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87], + pi: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35], claude: [1, 2, 3, 4, 5], codex: [1, 2, 3, 4, 5], + grok: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18], copilot: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90], cursor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], }; diff --git a/packages/paperclip-runner/src/index.ts b/packages/paperclip-runner/src/index.ts index 0a1744f0173..6b42469e678 100644 --- a/packages/paperclip-runner/src/index.ts +++ b/packages/paperclip-runner/src/index.ts @@ -109,7 +109,7 @@ export { RunnerdDotDriver, type RunnerdDotDriverOptions } from "./drivers/dot/ru export type { RunnerExecutionDriver } from "./control-plane/runner-execution-driver.js"; -export { bindRunnerLaunchResources, createRunnerProviderLaunch, runnerProviderLaunchRequirements, runnerLaunchRequirementsDigest, validatePreparedRunnerLaunch } from "./live/runner-provider-launch.js"; +export { RUNNER_GITHUB_LAUNCHER_VERSION, bindRunnerLaunchResources, createRunnerProviderLaunch, runnerProviderLaunchRequirements, runnerLaunchRequirementsDigest, validatePreparedRunnerLaunch } from "./live/runner-provider-launch.js"; export type { RunnerProviderLaunchRequirements, RunnerProviderLaunchEnvironment, PreparedRunnerProviderLaunch, RunnerLaunchPreparer } from "./live/runner-provider-launch.js"; export { resolveGrokDisableWebSearch } from "./drivers/acpx/grok-web-search.js"; diff --git a/packages/paperclip-runner/src/live/runner-provider-launch.test.ts b/packages/paperclip-runner/src/live/runner-provider-launch.test.ts index b924df89d74..d5c1fd22aa0 100644 --- a/packages/paperclip-runner/src/live/runner-provider-launch.test.ts +++ b/packages/paperclip-runner/src/live/runner-provider-launch.test.ts @@ -19,6 +19,41 @@ const environment: RunnerProviderLaunchEnvironment = { }; describe("built-in provider launch preparation", () => { + it("preserves probe project-instruction suppression through a prepared launch", () => { + const selected = runnerProviderLaunchRequirements({ provider: "codex", disableCodexProjectInstructions: true, + disableCodexWebSearch: true }, requirements.identity, + { model: requirements.model, permissions: "paperclip-runner-workspace-read-only" }, null, ""); + expect(selected.settings).toHaveProperty("disableCodexProjectInstructions", true); + const ordinary = runnerProviderLaunchRequirements({ provider: "codex", disableCodexWebSearch: true }, + requirements.identity, { model: requirements.model, permissions: "paperclip-runner-workspace-read-only" }, null, ""); + expect(runnerLaunchRequirementsDigest(selected)).not.toBe(runnerLaunchRequirementsDigest(ordinary)); + const launch = createRunnerProviderLaunch(selected, { ...environment, + options: { codexArgs: ["-c", "project_doc_max_bytes=8192", "app-server"], disableCodexProjectInstructions: false } }); + expect(launch.args).toEqual(["-c", "project_doc_max_bytes=8192", "-c", "project_doc_max_bytes=0", + "-c", 'web_search="disabled"', "app-server"]); + const prepared = { configurationId: "probe-policy", requirementsDigest: runnerLaunchRequirementsDigest(selected), provider: launch }; + expect(() => validatePreparedRunnerLaunch(selected, prepared)).not.toThrow(); + expect(() => validatePreparedRunnerLaunch(selected, { ...prepared, + provider: { ...launch, args: ["-c", 'web_search="disabled"', "app-server"] } })) + .toThrow("runner_prepared_launch_requirements_mismatch"); + }); + + it.each([undefined, false])("keeps ordinary project instructions when suppression is %s", disableCodexProjectInstructions => { + const selected = runnerProviderLaunchRequirements({ provider: "codex", disableCodexProjectInstructions }, + requirements.identity, { model: requirements.model }, null, ""); + expect(selected.settings).not.toHaveProperty("disableCodexProjectInstructions"); + expect(createRunnerProviderLaunch(selected, environment).args).toEqual(["app-server"]); + }); + + it("can suppress project instructions without disabling hosted search", () => { + const selected = runnerProviderLaunchRequirements({ provider: "codex", disableCodexProjectInstructions: true }, + requirements.identity, { model: requirements.model }, null, ""); + const provider = createRunnerProviderLaunch(selected, environment); + expect(provider.args).toEqual(["-c", "project_doc_max_bytes=0", "app-server"]); + expect(() => validatePreparedRunnerLaunch(selected, { configurationId: "probe", provider, + requirementsDigest: runnerLaunchRequirementsDigest(selected) })).not.toThrow(); + }); + it.each([true, false])("binds Grok search policy %s into the prepared descriptor", disabled => { const selected = runnerProviderLaunchRequirements({ provider: "acpx", acpxAgent: "grok", grokDisableWebSearch: disabled }, requirements.identity, { model: "grok-4.7" }, null, ""); diff --git a/packages/paperclip-runner/src/live/runner-provider-launch.ts b/packages/paperclip-runner/src/live/runner-provider-launch.ts index 39fa635f1d6..aa876191fc7 100644 --- a/packages/paperclip-runner/src/live/runner-provider-launch.ts +++ b/packages/paperclip-runner/src/live/runner-provider-launch.ts @@ -11,6 +11,10 @@ import type { DurableRecoveryIdentity } from "../contracts/durable-recovery.js"; import type { NativeRuntimeContextSnapshot } from "../contracts/runtime-context.js"; import type { CapabilityRunnerdCodexTransportOptions } from "./runnerd-codex-transport.js"; +// Environment plugins may advertise managed GitHub commands only with the +// matching Runner source that preserves helper selectors through Claude ACPX. +export const RUNNER_GITHUB_LAUNCHER_VERSION = 1; + /** Core's provider inputs. Executable paths and filesystem layout are supplied separately. */ export interface RunnerProviderLaunchRequirements { schema: "paperclip.runner.launch-requirements.v1"; @@ -23,7 +27,7 @@ export interface RunnerProviderLaunchRequirements { includeCollaborationModeInstructions: boolean; settings: Pick; + "piThinkingLevel" | "claudeEffort" | "grokEffort" | "grokDisableWebSearch" | "managedProfile" | "agentCoreProfile" | "opencodePermissionMode" | "disableCodexWebSearch" | "disableCodexProjectInstructions">; instructions: string; runtimeContext: NativeRuntimeContextSnapshot | null; } @@ -60,6 +64,7 @@ export function runnerProviderLaunchRequirements( managedProfile: options.managedProfile, agentCoreProfile: options.agentCoreProfile, opencodePermissionMode: options.opencodePermissionMode, ...(options.disableCodexWebSearch === true ? { disableCodexWebSearch: true } : {}), + ...(options.disableCodexProjectInstructions === true ? { disableCodexProjectInstructions: true } : {}), }, approvalPolicy: params.approvalPolicy === "on-request" || params.approvalPolicy === "untrusted" ? params.approvalPolicy : "never", @@ -110,13 +115,19 @@ export function validatePreparedRunnerLaunch( if (descriptor.kind !== requirements.provider || descriptor.model !== requirements.model) reject(); if (requirements.provider === "codex" && (descriptor.approvalPolicy !== requirements.approvalPolicy || Boolean(descriptor.readOnly) !== requirements.readOnly)) reject(); - if (requirements.provider === "codex" && requirements.settings.disableCodexWebSearch === true) { + if (requirements.provider === "codex" && (requirements.settings.disableCodexWebSearch === true + || requirements.settings.disableCodexProjectInstructions === true)) { if (!Array.isArray(descriptor.args) || descriptor.args.some(arg => typeof arg !== "string")) reject(); const args = descriptor.args as string[]; const commandIndex = args.indexOf("app-server"); const boundary = commandIndex < 0 ? args.length : commandIndex; - if (args.some(arg => arg === "--" || arg === "--search" || arg.startsWith("--search=")) || (commandIndex >= 0 && commandIndex !== args.length - 1) - || args[boundary - 2] !== "-c" || args[boundary - 1] !== 'web_search="disabled"') reject(); + const policy = [ + ...(requirements.settings.disableCodexProjectInstructions ? ["-c", "project_doc_max_bytes=0"] : []), + ...(requirements.settings.disableCodexWebSearch ? ["-c", 'web_search="disabled"'] : []), + ]; + if (args.some(arg => arg === "--" || (requirements.settings.disableCodexWebSearch + && (arg === "--search" || arg.startsWith("--search=")))) || (commandIndex >= 0 && commandIndex !== args.length - 1) + || JSON.stringify(args.slice(boundary - policy.length, boundary)) !== JSON.stringify(policy)) reject(); } if (requirements.provider === "acpx") { const policy = descriptor.providerPolicy as Record | undefined; @@ -324,7 +335,7 @@ export function createRunnerProviderLaunch( runtimeStateRoot: input.options.runnerFilesystemRoot ?? input.root, codexCommand: effectiveCodexCommand, instructionWorkingCopyRoot: runtimeContext?.instructions.workingCopy?.rootPath, - disableProjectInstructions: input.options.disableCodexProjectInstructions, + disableProjectInstructions: settings.disableCodexProjectInstructions ?? input.options.disableCodexProjectInstructions, readOnlyRoots: [ ...trustedRuntimeReadOnlyRoots( input.options.environment, @@ -429,7 +440,6 @@ export function createRunnerdCodexAppServerArgs(input: { // isolated runner home; a stale controller environment must never cause the // execution workspace itself to become an explicit deny root. const args = input.args ? [...input.args] : [ - ...(input.disableProjectInstructions ? ["-c", "project_doc_max_bytes=0"] : []), ...createIsolatedCodexAppServerArgs( { ...input.environment, @@ -444,17 +454,19 @@ export function createRunnerdCodexAppServerArgs(input: { if (!input.args && usesPreconfiguredHome(input.environment) && input.workingDirectory) { args.unshift("-c", `projects={${JSON.stringify(input.workingDirectory)}={trust_level="trusted"}}`); } - if (input.disableWebSearch) { + if (input.disableWebSearch || input.disableProjectInstructions) { // Apply Core's restriction after host overrides. Keep the override before // the Codex subcommand and after any earlier provider-home configuration. const commandIndex = args.indexOf("app-server"); // Restricted launches use the owned app-server argument shape. Refuse // trailing subcommand flags and option terminators rather than guessing // whether another CLI spelling could override or neutralize this policy. - if (args.some(arg => arg === "--" || arg === "--search" || arg.startsWith("--search=")) || (commandIndex >= 0 && commandIndex !== args.length - 1)) { - throw new Error("runner_codex_web_search_policy_override"); + if (args.some(arg => arg === "--" || (input.disableWebSearch && (arg === "--search" || arg.startsWith("--search=")))) || (commandIndex >= 0 && commandIndex !== args.length - 1)) { + throw new Error(input.disableWebSearch ? "runner_codex_web_search_policy_override" : "runner_codex_project_instructions_policy_override"); } - args.splice(commandIndex < 0 ? args.length : commandIndex, 0, "-c", 'web_search="disabled"'); + args.splice(commandIndex < 0 ? args.length : commandIndex, 0, + ...(input.disableProjectInstructions ? ["-c", "project_doc_max_bytes=0"] : []), + ...(input.disableWebSearch ? ["-c", 'web_search="disabled"'] : [])); } return args; } diff --git a/packages/paperclip-runner/src/providers/grok/launcher.cjs b/packages/paperclip-runner/src/providers/grok/launcher.cjs index d936c0f8e1d..e5aa3ff075b 100755 --- a/packages/paperclip-runner/src/providers/grok/launcher.cjs +++ b/packages/paperclip-runner/src/providers/grok/launcher.cjs @@ -1,4 +1,4 @@ -// Grok admission source identity: sha256:7dfa4769aab33e8625b009b4f3efe59e0c6180dba43ebb486179638b9fbaa216 +// Grok admission source identity: sha256:5abf25aa2d101b7d58745c8d8b362c95033713a137a7b27204439a98d130036c // This launcher is admitted by digest and receives a verified native executable. // Never resolve an executable through PATH or accept command-line overrides. const { isAbsolute, join } = require("node:path"); diff --git a/packages/paperclip-runner/test-fixtures/pi-acp/profile-v36-identity.json b/packages/paperclip-runner/test-fixtures/pi-acp/profile-v36-identity.json new file mode 100644 index 00000000000..338d01d6c28 --- /dev/null +++ b/packages/paperclip-runner/test-fixtures/pi-acp/profile-v36-identity.json @@ -0,0 +1,52 @@ +{ + "commandDigest": "sha256:faafb66c09c1efc7eb4eda6baaa5813c975e8a992f9cd333687695aa5522d7dc", + "declaration": { + "schema": "paperclip.acpx_profile_declaration.v1", + "agent": "pi", + "agentProfileVersion": 36, + "acpxVersion": "0.13.1", + "agentServerVersion": "0.0.33", + "agentRuntimeVersion": "1.0.0", + "nodeVersion": "24.21.0", + "closure": { + "darwin-arm64": "282022db10150c6632b3444df421342e7d534bdf5d5fb1097a2e79d0625a2bcf", + "darwin-x64": "64e251e19009f755c0b04f73ce2138246faab71a961b0f13d75ebfcc34bef12e", + "linux-x64": "713b1fdff42fb56a1518bdc084f181d70bee8ebadc3e4b1d76321ed9108c8410" + }, + "wrapperSha256": "9d129b3d38772e93e97080aa6c4e574ac5df4e47ce5bc331a484a9fbf8188b36", + "helperSha256": "41e0490b617da0d60e0c8ec58ef311236f945129d99f816cff6897f78da7f91d", + "extensionSha256": "f5818573a355702388072b28926897db546a5cf346b5171f646582ce5f9102a4", + "agentFilesBinding": "registered-runtime-context-v1", + "nativeQuestions": "select-confirm-input-editor-v4-root-field-types", + "pathPolicy": "sdk-normalization-v1", + "mcpToolNames": "provider-safe-exact-source-v1", + "acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "nativeAssistantMessages": "pi1-rpc-deltas-v3-bound-message-and-tool-receipts", + "nativeNotices": "session-extension-v2-bounded-runtime-failure", + "messageProjectionSha256": "17d1e91b948dfa6a56e1905645add52b502041ad843c8d50a13b07956f22aadd", + "noticeProjectionSha256": "014485adc690998e395d334ea3ae67f72be6060a2fc78a1185df7b8d1da28fbf", + "sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1", + "nativeInputDisposition": "queued-only-v1", + "cacheWarming": "disabled-owned-decision-v1", + "thinkingLevel": { + "supported": [ + "off", + "low", + "high", + "max" + ], + "newConfigurationDefault": "low", + "admission": "explicit-native-effective-before-prompt-v1", + "recovery": "mode-bound-v1" + }, + "modelSelection": "caller-selected-native-verified-v1", + "providerConfigurationSourceSha256": "75a9a13f13f224dc092bd8ebf49e8747162172943761e44476c8eb18d80c52d4", + "credentialEnvironmentSourceSha256": "7b1dc57c41b0c77bae759e414e486914f9cde9b1ef0802965de2982fb82cf7d2", + "runtimeSandboxSourceSha256": "b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "recoveryIdentitySourceSha256": "f74d061cc07b0c7ca04df62ca991d940a733d9ef6594b3362451769fc10030b8", + "nativeCancellation": "acknowledged-cancelled-terminal-preserves-usage-v1", + "dependencySecurityPatchSha256": "5273a195b952f233336122842faaf650193c04f36b9359d20d4d63079b0ce814", + "openRouterAttributionSourceSha256": "1211191529d6073b733b311c3d69cada4973eede0f3e540c46e261f78e0b90f9", + "preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e" + } +} diff --git a/packages/paperclip-runner/test/fixtures/copilot-profile-v89-identity.json b/packages/paperclip-runner/test/fixtures/copilot-profile-v89-identity.json new file mode 100644 index 00000000000..1ce2390d890 --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/copilot-profile-v89-identity.json @@ -0,0 +1,88 @@ +{ + "commandDigest": "sha256:a64cd9fdd1e2c36aca0c078722ad96293e1d6f13c5eed220cf6d6a689f52cce3", + "declaration": { + "schema": "paperclip.acpx_profile_declaration.v1", + "agent": "copilot", + "agentProfileVersion": 89, + "acpxVersion": "0.13.1", + "agentServerVersion": "1.0.88", + "protocolPolicyRevision": "copilot-agent-manual-v1", + "acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3", + "distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107", + "agentFilesBinding": "registered-runtime-context-v1", + "systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1", + "sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1", + "innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a", + "upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0", + "patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679", + "messageIdentityContract": "copilot-native-message-id-v1", + "ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1", + "permissionContextContract": "copilot-edit-permission-context-v1", + "permissionContextSourceSha256": "de2d38d702b3c574161af0c0c04790fb6f6347916eb3916ce7a131a8c229121b", + "permissionAdapterSourceSha256": "ee5929ed2ee647ad0369465dee9d51a00b2dd28e1ff735b43e969ddc9c164668", + "permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9", + "permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f", + "permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4", + "semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2", + "semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791", + "semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5", + "toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706", + "semanticNormalizedInputContract": "validated-forwarded-input-commit-v1", + "semanticSidecarSourceSha256": "b6d5888f47a47838c507a43fbe31a778ac63d4fcbcdc9a869108f604c34c3824", + "semanticDirectDriverSourceSha256": "4ec65aa1d4e905a1e732f2d043bbd39de1ab92f380276ede37d6a72efc19c64d", + "semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975", + "semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e", + "semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59", + "semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb", + "semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b", + "semanticSidecarProtocolSourceSha256": "26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab", + "semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1", + "displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877", + "displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e", + "configuredEnvironmentSourceSha256": "63d14f53e776ad6c13e2b5577df6fb37a685142e1866f3ab5b5fc9d82c9c19fb", + "configuredEnvironmentRustSourceSha256": "1e683a12f94d75e07885d1f8fc670c7f128093c3cfe116ec57b62cf2dda8d40b", + "profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282", + "profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571", + "profileInstallationSourceSha256": "58841b2d3b47ab9956db20608802edcd0e55cf5856002dc7c51e05e75bdcb8f6", + "attachmentPolicySourceSha256": "4f7638b7cc64e18f6a4ba33fd46de2b3d49225e32aeab41f5c695b268fa4f8e2", + "singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f", + "nativeSessionRuntimeSourceSha256": "cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea", + "nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842", + "nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1", + "nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90", + "harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5", + "harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249", + "harnessSessionSourceSha256": "bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851", + "transportContractSourceSha256": "06f06c53ecae304f72034ffb2f659ff087c82a70ed4994daf21a9d2edaf56e4e", + "nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586", + "nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44", + "nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b", + "providerLaunchSourceSha256": "d3a1498d43abf2e372b68b8d429a7f11dcf30c76fc49bc6639ad2dfa69079943", + "launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296", + "runtimeRequestBridgeSourceSha256": "8bf5c2ed6d6223144f7eb777e97973655301f5be10d9734f025cfc2b0bdff431", + "runtimeRequestFacadeSourceSha256": "23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa", + "runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211", + "guardedInstallationSourceSha256": "64b137e3ef695e7d03013ea2b6c8830171e0a73e0bf2e73d55e9ced57a47bcf6", + "nativeExecutionSourceSha256": "116c348e232848a80c5556494c109314baafb20fb5c97133d9656c7732ee54be", + "profileCompatibilitySourceSha256": "3b5f3019a82d1a292527785ce17f16f8b9ca60977fe525b7ae7c150241e801f8", + "installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d", + "assetOwnershipSourceSha256": "e5bb6bc58b3f0e957a64b2cd99c710337ffcd9c0e1fadb341f48e033b64b99f5", + "assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f", + "credentialBindingSourceSha256": "7b1dc57c41b0c77bae759e414e486914f9cde9b1ef0802965de2982fb82cf7d2", + "admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456", + "metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727", + "permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1", + "semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811", + "preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e", + "runtimeHostSourceSha256": "26b2e115b3f4ae7c20e9d963d117e0286c958af81386deade83397f12abf9662", + "runtimeAdapterSourceSha256": "fab1e278e0f2aed55b35e57d14329eb431b79a08040fa079aa2403842d16085e", + "runtimeSandboxSourceSha256": "b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca", + "agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba", + "nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b" + } +} diff --git a/packages/paperclip-runner/test/fixtures/copilot-profile-v90-identity.json b/packages/paperclip-runner/test/fixtures/copilot-profile-v90-identity.json new file mode 100644 index 00000000000..76fb2fdb327 --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/copilot-profile-v90-identity.json @@ -0,0 +1,88 @@ +{ + "commandDigest": "sha256:53016e6c7ccd5ea3e2602f98173cd176843e2f8e8376b2886bdec0576309c580", + "declaration": { + "schema": "paperclip.acpx_profile_declaration.v1", + "agent": "copilot", + "agentProfileVersion": 90, + "acpxVersion": "0.13.1", + "agentServerVersion": "1.0.88", + "protocolPolicyRevision": "copilot-agent-manual-v1", + "acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3", + "distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107", + "agentFilesBinding": "registered-runtime-context-v1", + "systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1", + "sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1", + "innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a", + "upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0", + "patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679", + "messageIdentityContract": "copilot-native-message-id-v1", + "ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1", + "permissionContextContract": "copilot-edit-permission-context-v1", + "permissionContextSourceSha256": "de2d38d702b3c574161af0c0c04790fb6f6347916eb3916ce7a131a8c229121b", + "permissionAdapterSourceSha256": "ee5929ed2ee647ad0369465dee9d51a00b2dd28e1ff735b43e969ddc9c164668", + "permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9", + "permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f", + "permissionClassifierSourceSha256": "1161eabd82e26133e3b3db3ab567f65d52808b03f176e93497e02c8330349fb4", + "semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2", + "semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791", + "semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5", + "toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706", + "semanticNormalizedInputContract": "validated-forwarded-input-commit-v1", + "semanticSidecarSourceSha256": "b6d5888f47a47838c507a43fbe31a778ac63d4fcbcdc9a869108f604c34c3824", + "semanticDirectDriverSourceSha256": "4ec65aa1d4e905a1e732f2d043bbd39de1ab92f380276ede37d6a72efc19c64d", + "semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975", + "semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e", + "semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59", + "semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb", + "semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b", + "semanticSidecarProtocolSourceSha256": "26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab", + "semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1", + "displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877", + "displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e", + "configuredEnvironmentSourceSha256": "63d14f53e776ad6c13e2b5577df6fb37a685142e1866f3ab5b5fc9d82c9c19fb", + "configuredEnvironmentRustSourceSha256": "1e683a12f94d75e07885d1f8fc670c7f128093c3cfe116ec57b62cf2dda8d40b", + "profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282", + "profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571", + "profileInstallationSourceSha256": "58841b2d3b47ab9956db20608802edcd0e55cf5856002dc7c51e05e75bdcb8f6", + "attachmentPolicySourceSha256": "4f7638b7cc64e18f6a4ba33fd46de2b3d49225e32aeab41f5c695b268fa4f8e2", + "singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f", + "nativeSessionRuntimeSourceSha256": "cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea", + "nativeRuntimeContextSourceSha256": "71607f80dbb8dc5cc6c123d0b8f3995d6c5407277695a8c82cbedc90fb8f4842", + "nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1", + "nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90", + "harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5", + "harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249", + "harnessSessionSourceSha256": "bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851", + "transportContractSourceSha256": "06f06c53ecae304f72034ffb2f659ff087c82a70ed4994daf21a9d2edaf56e4e", + "nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586", + "nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44", + "nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b", + "providerLaunchSourceSha256": "4acf2a9c0ab21353f147e7a8924c19c7e3679c3291d8427891e3538ce67cd695", + "launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296", + "runtimeRequestBridgeSourceSha256": "8bf5c2ed6d6223144f7eb777e97973655301f5be10d9734f025cfc2b0bdff431", + "runtimeRequestFacadeSourceSha256": "23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa", + "runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211", + "guardedInstallationSourceSha256": "64b137e3ef695e7d03013ea2b6c8830171e0a73e0bf2e73d55e9ced57a47bcf6", + "nativeExecutionSourceSha256": "116c348e232848a80c5556494c109314baafb20fb5c97133d9656c7732ee54be", + "profileCompatibilitySourceSha256": "633a15921f5046f5036f556c43ac7e27679e022564890863a03ee3bdbbcd3561", + "installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d", + "assetOwnershipSourceSha256": "e5bb6bc58b3f0e957a64b2cd99c710337ffcd9c0e1fadb341f48e033b64b99f5", + "assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f", + "credentialBindingSourceSha256": "7b1dc57c41b0c77bae759e414e486914f9cde9b1ef0802965de2982fb82cf7d2", + "admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456", + "metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727", + "permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1", + "semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811", + "preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e", + "runtimeHostSourceSha256": "26b2e115b3f4ae7c20e9d963d117e0286c958af81386deade83397f12abf9662", + "runtimeAdapterSourceSha256": "fab1e278e0f2aed55b35e57d14329eb431b79a08040fa079aa2403842d16085e", + "runtimeSandboxSourceSha256": "b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca", + "agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba", + "nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b" + } +} diff --git a/packages/paperclip-runner/test/fixtures/copilot-profile-v91-identity.json b/packages/paperclip-runner/test/fixtures/copilot-profile-v91-identity.json new file mode 100644 index 00000000000..84e0a00473b --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/copilot-profile-v91-identity.json @@ -0,0 +1,89 @@ +{ + "commandDigest": "sha256:e4383c48ad21898dfe38609fdc8c0fa9a76dfcb8582a64b5415f107846904aa9", + "declaration": { + "schema": "paperclip.acpx_profile_declaration.v1", + "agent": "copilot", + "agentProfileVersion": 91, + "acpxVersion": "0.13.1", + "agentServerVersion": "1.0.88", + "protocolPolicyRevision": "copilot-agent-manual-v1", + "acpxPatchSha256": "1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "policySha256": "a4be69be43db84052560420a3ca546a665c9165573c24d543eb33d144ba138a3", + "distributionSourceSha256": "c0a65e247fc47b263bd369a3d5ab505c4b323d4e6493be8a9ecbeb0e9dbb9107", + "agentFilesBinding": "registered-runtime-context-v1", + "systemInstructionDelivery": "COPILOT_HOME/copilot-instructions.md:replace-under-lifetime-lease-before-launch:v1", + "sharedRuntimeContract": "paperclip.acpx-runtime-contract.v1", + "innerDistributionSourceSha256": "9bb08347eed01f1ab44e065c8b06ead9be3a25973d6e6329dc4c0a099096279a", + "upstreamAppSha256": "7b48282a19b5b0814a0c96ad5e3a125173d792cc55f9525b2effea962f6063c0", + "patchedAppSha256": "61f8eebe0c30cc03b311c6818b2acb158a6d16ca58c8bfac5ebd42db095c2679", + "messageIdentityContract": "copilot-native-message-id-v1", + "ownedDistributionDelivery": "COPILOT_CLI_DIST_DIR:lease-owned-guarded-inner-distribution:v1", + "permissionContextContract": "copilot-edit-permission-context-v1", + "permissionContextSourceSha256": "de2d38d702b3c574161af0c0c04790fb6f6347916eb3916ce7a131a8c229121b", + "permissionAdapterSourceSha256": "ee5929ed2ee647ad0369465dee9d51a00b2dd28e1ff735b43e969ddc9c164668", + "permissionLocationsSourceSha256": "f39a81da395b6e23aa2a1a45572a28846e5e81bfd3d764d9a456f74a79c752e9", + "permissionRedactionSourceSha256": "ad6770c69269087951790e2e87e22ac088e3d432769adf17ebc865ed512e6c4f", + "permissionClassifierSourceSha256": "b60e4b924e827e122860d2a4cc13b465ba748ee521620887c88f3c58c0df6081", + "semanticToolReceiptContract": "paperclip.semantic_tool_receipt.v2", + "semanticReceiptSourceSha256": "b2795726b2234a663fec1a6ca11f0551ae69882fe9e82aa39686e952d036e791", + "semanticBridgeSourceSha256": "8764b8c9032fdc7d1415b01e75a9fc57c550e7a5e11da4bed7d9100082872ae5", + "toolEvidenceSourceSha256": "6eef5039afc75bb851eaf9cd82a6f099e4bd398ca2deb59dd66f5b2a81a7a706", + "semanticNormalizedInputContract": "validated-forwarded-input-commit-v1", + "semanticSidecarSourceSha256": "a5b81494bdae7c100b71c3a8024f04287c458c2e549eb094e3501168b54fa536", + "semanticDirectDriverSourceSha256": "4ec65aa1d4e905a1e732f2d043bbd39de1ab92f380276ede37d6a72efc19c64d", + "semanticValidationSourceSha256": "02fbb92681c78c5de5d2f1aae25aabc25a0e5df6b0ff4baccac652192a8da975", + "semanticNormalizationSourceSha256": "8b628b82e8eacc91cdcc6fdca2210ba4cf282f01e9c6f74e95ff271655b5047e", + "semanticCompletionContractSourceSha256": "f49398b682a5623812f5d3c33d6bb9a749008e47cc0ed969072041615f66df59", + "semanticValidatorsSourceSha256": "b20ec8ee6e7165117233ce4421a975bb9d3f276220ce31655c0b9a717cb2d9cb", + "semanticSchemaBundleSourceSha256": "81ef98ca7b34896a0f76390ac7af3dd8ef7609fe6f38d54bd6a69373d50fe40b", + "semanticSidecarProtocolSourceSha256": "26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab", + "semanticSidecarReceiptCommitContract": "correlated-tool-resolve-v1", + "displayEventSourceSha256": "d72f9a6035c247bc6a42c067c05753076c54322c4b58984e93922f3325dc6877", + "displayProjectionSourceSha256": "75952606b26dd93bbc582c1f0a4424487377272ea745fb3a27ceae031fd5dc7e", + "configuredEnvironmentSourceSha256": "63d14f53e776ad6c13e2b5577df6fb37a685142e1866f3ab5b5fc9d82c9c19fb", + "configuredEnvironmentRustSourceSha256": "1e683a12f94d75e07885d1f8fc670c7f128093c3cfe116ec57b62cf2dda8d40b", + "profileActivitySourceSha256": "4fdb6b0f9125fd8372ba6f27945fa5c8a3d3b2ec891df544092e3dc7a75d5282", + "profileExtensionsSourceSha256": "a80ddc95eb4505095bfdbbf85c758199949ab20be9c84b34c77f762eb7180571", + "profileInstallationSourceSha256": "58841b2d3b47ab9956db20608802edcd0e55cf5856002dc7c51e05e75bdcb8f6", + "attachmentPolicySourceSha256": "4f7638b7cc64e18f6a4ba33fd46de2b3d49225e32aeab41f5c695b268fa4f8e2", + "singleReadEvidenceSourceSha256": "4069393405089bde613cecec04f5123c426594811da4c668bd96f0e7e283550f", + "nativeSessionRuntimeSourceSha256": "cbcefbceb0b2a24b0909cbb8361df6b8853b4f50b21d66328bc483951a8246ea", + "nativeRuntimeContextSourceSha256": "152d7187ce1dc91a15377d368776555c9897d6e018b65f0f59a92dcdbd6497d9", + "nativeRuntimeContextFieldsSourceSha256": "069f4bc3c4dc95888f30d3fea331adb0ee0a164d09f4f70ec9c132f24f69c7dd", + "nativeRuntimePromptsSourceSha256": "fff5755dba9b7dce8a5f10ff6ac6d5ed06b92ed079ce1e0a44ac72f2ce7869c1", + "nativeSessionContractSourceSha256": "22a8c96ce41ba4f03e976fc37f0cc7c5d05cee99fa789941da31ee1dab440b90", + "harnessSessionContractSourceSha256": "baba1a210b863518f01e0c875606ed37f9467c433222251438578bd999bc43b5", + "harnessBackendSourceSha256": "dc827e281ae6731545b166ee4d709a4f67f594a556dcbe63c70fe8f1aca26249", + "harnessSessionSourceSha256": "bb9bb3daac53ce5955ddf8f56839c7a57ae60c74ea3cdd08895ab6912fcf7851", + "transportContractSourceSha256": "06f06c53ecae304f72034ffb2f659ff087c82a70ed4994daf21a9d2edaf56e4e", + "nativeBackendSourceSha256": "7495d9898d22dca1ebf6336361d332bafefc4498f6e49f1135025fad846c8586", + "nativeBackendFactorySourceSha256": "7e431c926b76d53c4c08e3a4105353dcd34bd940f6588a1a68f79631f6feeb44", + "nativeInstructionsSourceSha256": "82ac6e5f7275b4ef79fb166c7330efc504e80cab3d7e9081e4e40a8b570bc50b", + "providerLaunchSourceSha256": "4acf2a9c0ab21353f147e7a8924c19c7e3679c3291d8427891e3538ce67cd695", + "launchPreparationStoreSourceSha256": "ff6025568bbc0aa6ce545772d73dfca2d3da226740bd3a7b386b069e76cff296", + "runtimeRequestBridgeSourceSha256": "8bf5c2ed6d6223144f7eb777e97973655301f5be10d9734f025cfc2b0bdff431", + "runtimeRequestFacadeSourceSha256": "23361d25e660ef7919bfb54f21727b899e7d6f8865307604ee15eca7488afeaa", + "runtimeRequestNotificationSourceSha256": "6196c6175f11c69295dc00fc773201f4fdeca26c500653870253c43b81e65211", + "guardedInstallationSourceSha256": "64b137e3ef695e7d03013ea2b6c8830171e0a73e0bf2e73d55e9ced57a47bcf6", + "nativeExecutionSourceSha256": "116c348e232848a80c5556494c109314baafb20fb5c97133d9656c7732ee54be", + "profileCompatibilitySourceSha256": "b6cb103f69fe4f9a54116089082fa9bef8523e8d19bb03fe8645bd98133f1836", + "installationSourceSha256": "a1c7c6f103b898216973e77ffbac0ef7572e250aa574d1a4065270e9dfa0dc2d", + "assetOwnershipSourceSha256": "e5bb6bc58b3f0e957a64b2cd99c710337ffcd9c0e1fadb341f48e033b64b99f5", + "assetInstallerSourceSha256": "d3bd9514e5920d0cc3567aa50487fb534c57a6f66b8da0d3f22c5f6e499c841f", + "credentialBindingSourceSha256": "7b1dc57c41b0c77bae759e414e486914f9cde9b1ef0802965de2982fb82cf7d2", + "admissionDiagnosticsSourceSha256": "df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "grokAdmissionSourceSha256": "f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "sidecarErrorSourceSha256": "17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "profileSourceSha256": "f5538b3dbefa7634a332b87d5ededcefb56aa7e5790cede1c7a0d948a548a456", + "metadataProbeSourceSha256": "be4c8416edd27061109bc1f33d0c26071b4facbb3f651beaae6b3c0275cce727", + "permissionToolIdentitySourceSha256": "770b47305e571a81f344883057d0e4a482a1cd59ac77d045836593a8e739deb1", + "semanticContinuationSourceSha256": "c9cc0fd516b3df92a6655f56ee9bef37615a889c371e2e137d8ac5c1176de811", + "preparedHarnessHomeSourceSha256": "bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e", + "runtimeHostSourceSha256": "26b2e115b3f4ae7c20e9d963d117e0286c958af81386deade83397f12abf9662", + "runtimeAdapterSourceSha256": "fab1e278e0f2aed55b35e57d14329eb431b79a08040fa079aa2403842d16085e", + "runtimeSandboxSourceSha256": "b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "cancellationSourceSha256": "a07d1a9e982dec0ae130c1ad1efa5bc75c6a39e013b730ba3ee45256f09ceeca", + "agentFilesSourceSha256": "fb32f6badc13dfa22f231479509b631b2f0eba0c99b1ab9f3cd5c0c601aa03ba", + "nativeDistributionSourceSha256": "aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b" + } +} diff --git a/packages/paperclip-runner/test/fixtures/grok-profile-v16-identity.json b/packages/paperclip-runner/test/fixtures/grok-profile-v16-identity.json new file mode 100644 index 00000000000..5953a8356e8 --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/grok-profile-v16-identity.json @@ -0,0 +1,35 @@ +{ + "commandDigest": "sha256:6d645a201f5bfc8848941dc87174e56ab2f93ae4be82c9f61d8de57841ceffd4", + "sourceDigest": "sha256:ecc958dba3040b1ab7a1fe3658a2d6907f47667cf9477296f7679086ac301947", + "declaration": { + "schema": "paperclip.grok-profile-identity.v1", + "agent": "grok", + "agentProfileVersion": 16, + "agentServerPackage": "builtin:grok-acp", + "agentServerVersion": "1", + "agentRuntimePackage": "native:grok", + "agentRuntimeVersion": "1.0.13", + "requiresProviderPolicy": false, + "acpxVersion": "0.13.1", + "sources": { + "patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:abe3e6c718e8e0fed154526affc94e34b46d96f7bdaed5d88e2b00a21a5b84db", + "packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c", + "packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73", + "packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:fab1e278e0f2aed55b35e57d14329eb431b79a08040fa079aa2403842d16085e", + "packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:26b2e115b3f4ae7c20e9d963d117e0286c958af81386deade83397f12abf9662", + "packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e", + "packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab", + "packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b", + "packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55", + "packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:372328d8666597fd49842ed4525784e7839be263911eafbd36353fe24058e18d", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:09be94877a11ff526f4351b36471ed9685828a3ee4f8e6d329031e88c713afcb", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:bfbc991b1d4efed177ddb6879b38c5aa6a39a8228d153d21d590c060e522a40a" + } + } +} diff --git a/packages/paperclip-runner/test/fixtures/grok-profile-v17-identity.json b/packages/paperclip-runner/test/fixtures/grok-profile-v17-identity.json new file mode 100644 index 00000000000..3a9173d4033 --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/grok-profile-v17-identity.json @@ -0,0 +1,35 @@ +{ + "commandDigest": "sha256:c1d89599a6e621cc9ebf3a0370f6f2433d8999feaead5b6b7dced0da6e5d199e", + "sourceDigest": "sha256:ddfd2e181ae18b59165e314bdcbb25c675985b2be4a1ed157c765505010c983b", + "declaration": { + "schema": "paperclip.grok-profile-identity.v1", + "agent": "grok", + "agentProfileVersion": 17, + "agentServerPackage": "builtin:grok-acp", + "agentServerVersion": "1", + "agentRuntimePackage": "native:grok", + "agentRuntimeVersion": "1.0.13", + "requiresProviderPolicy": false, + "acpxVersion": "0.13.1", + "sources": { + "patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:b6d5888f47a47838c507a43fbe31a778ac63d4fcbcdc9a869108f604c34c3824", + "packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c", + "packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73", + "packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:fab1e278e0f2aed55b35e57d14329eb431b79a08040fa079aa2403842d16085e", + "packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:26b2e115b3f4ae7c20e9d963d117e0286c958af81386deade83397f12abf9662", + "packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e", + "packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab", + "packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b", + "packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55", + "packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:af0aabac999746b38f997b0d8e19e9d0e8434f6886038169bad3ab7610e5c80f", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:bfbc991b1d4efed177ddb6879b38c5aa6a39a8228d153d21d590c060e522a40a" + } + } +} diff --git a/packages/paperclip-runner/test/fixtures/grok-profile-v18-identity.json b/packages/paperclip-runner/test/fixtures/grok-profile-v18-identity.json new file mode 100644 index 00000000000..a2ddd70b50f --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/grok-profile-v18-identity.json @@ -0,0 +1,35 @@ +{ + "commandDigest": "sha256:fa1f17185ca8f56bb479d1cb64fd753c75b6f460408f5c624b3ebf8848c5268f", + "sourceDigest": "sha256:36635891feb73654f25bfffb6e5956535609a50897a60030ca1e4d127a92343e", + "declaration": { + "schema": "paperclip.grok-profile-identity.v1", + "agent": "grok", + "agentProfileVersion": 18, + "agentServerPackage": "builtin:grok-acp", + "agentServerVersion": "1", + "agentRuntimePackage": "native:grok", + "agentRuntimeVersion": "1.0.13", + "requiresProviderPolicy": false, + "acpxVersion": "0.13.1", + "sources": { + "patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:b6d5888f47a47838c507a43fbe31a778ac63d4fcbcdc9a869108f604c34c3824", + "packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c", + "packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73", + "packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:fab1e278e0f2aed55b35e57d14329eb431b79a08040fa079aa2403842d16085e", + "packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:26b2e115b3f4ae7c20e9d963d117e0286c958af81386deade83397f12abf9662", + "packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e", + "packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab", + "packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b", + "packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55", + "packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:af0aabac999746b38f997b0d8e19e9d0e8434f6886038169bad3ab7610e5c80f", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:8cc60461e639198813b596fa271ac8b78ac22c89adaf7a8352dc88dfb2d484bd" + } + } +} diff --git a/packages/paperclip-runner/test/fixtures/grok-profile-v19-identity.json b/packages/paperclip-runner/test/fixtures/grok-profile-v19-identity.json new file mode 100644 index 00000000000..ce51f27a769 --- /dev/null +++ b/packages/paperclip-runner/test/fixtures/grok-profile-v19-identity.json @@ -0,0 +1,36 @@ +{ + "commandDigest": "sha256:5205c2197b94cfbd3e62fbeea027f2e80d092f736ca52656dda2f7044ba9a5ca", + "sourceDigest": "sha256:5abf25aa2d101b7d58745c8d8b362c95033713a137a7b27204439a98d130036c", + "declaration": { + "schema": "paperclip.grok-profile-identity.v1", + "agent": "grok", + "agentProfileVersion": 19, + "agentServerPackage": "builtin:grok-acp", + "agentServerVersion": "1", + "agentRuntimePackage": "native:grok", + "agentRuntimeVersion": "1.0.13", + "requiresProviderPolicy": false, + "acpxVersion": "0.13.1", + "sources": { + "patches/acpx@0.13.1.patch": "sha256:1e7dae8c83bf8620f53f2bea13c175eeff95e7e955783f6f529bb1dcbdcdb6d2", + "packages/paperclip-runner/src/cli/acpx-runtime-sidecar.ts": "sha256:a5b81494bdae7c100b71c3a8024f04287c458c2e549eb094e3501168b54fa536", + "packages/paperclip-runner/src/cli/acpx-sidecar-input.ts": "sha256:17948ad74350dc487ab20d24a0fb89daf0b426b98396454fff0e3856e922d82c", + "packages/paperclip-runner/src/drivers/acpx/admission-diagnostics.ts": "sha256:df8f1aa4f2d1bc63b2635bdd09cb8165c8b7681ddcc2618435c7c08ca90475af", + "packages/paperclip-runner/src/drivers/acpx/grok-effort.ts": "sha256:f1e46cf2e1e36b7704bd797b678e3db8db2003f97bdb9d827cfa3d7662698777", + "packages/paperclip-runner/src/drivers/acpx/grok-web-search.ts": "sha256:754b5ca2345c045421f6007f34999508d996e1cea035f0f1952b3d5f00072b9c", + "packages/paperclip-runner/src/drivers/acpx/grok-credentials.ts": "sha256:1c4ff0be6da798b6fcc604d20a9198dcc8523a3806d563b5b5345b7260549f73", + "packages/paperclip-runner/src/drivers/acpx/codex-runtime-adapter.ts": "sha256:fab1e278e0f2aed55b35e57d14329eb431b79a08040fa079aa2403842d16085e", + "packages/paperclip-runner/src/drivers/acpx/runtime-host.ts": "sha256:26b2e115b3f4ae7c20e9d963d117e0286c958af81386deade83397f12abf9662", + "packages/paperclip-runner/src/drivers/preconfigured-home.ts": "sha256:bb286f7ddc6e9597c56d46bf7ff6e3b556c5b680d7e147fd46518724432b083e", + "packages/paperclip-runner/src/drivers/acpx/runtime-sandbox.ts": "sha256:b9f05e522f92b6bade51c7a17f8d28bf31ab9db26510b8d5ddce8519d563a875", + "packages/paperclip-runner/src/drivers/acpx/sidecar-protocol.ts": "sha256:26b6b21dad582810d00f46e947e682a9615ee9761c487f6315970d6d014c05ab", + "packages/paperclip-runner/src/drivers/acpx/generated-sidecar-contract.ts": "sha256:b60e4b924e827e122860d2a4cc13b465ba748ee521620887c88f3c58c0df6081", + "packages/paperclip-runner/src/drivers/acpx/native-distribution-integrity.ts": "sha256:aed327c389e957483a503cb1bb8b247ee7cd7ff4b528fb4567c39bcb4577690b", + "packages/paperclip-runner/src/providers/grok/launcher.cjs": "sha256:f0b698395a3704ed2ffaf84ea19bdb20c36c8a0a70b7c629c7b6ffe144e59e55", + "packages/paperclip-runner/src/providers/grok/platforms.json": "sha256:931eed3abbf2bf041e6c72e6e9486d976f4352613cf069b216ddf010c9a8e704", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_backend.rs": "sha256:af0aabac999746b38f997b0d8e19e9d0e8434f6886038169bad3ab7610e5c80f", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_provider_session.rs": "sha256:fc8d7cce62e0c02efddece8696e8d24f6a9fe19e5288a5e67a01ffbf931a3f6e", + "packages/paperclip-runner/runner/crates/runner-core/src/acpx_sidecar_transport.rs": "sha256:8cc60461e639198813b596fa271ac8b78ac22c89adaf7a8352dc88dfb2d484bd" + } + } +} diff --git a/packages/plugins/sdk/src/environment-tasks.ts b/packages/plugins/sdk/src/environment-tasks.ts index 857d2277efb..5154431ca0e 100644 --- a/packages/plugins/sdk/src/environment-tasks.ts +++ b/packages/plugins/sdk/src/environment-tasks.ts @@ -46,6 +46,7 @@ export const environmentRunnerLaunchRequirementsSchema = z.object({ claudeEffort: z.enum(["low", "medium", "high", "xhigh", "max"]).optional(), opencodePermissionMode: z.enum(["allow", "ask", "deny"]).optional(), disableCodexWebSearch: z.literal(true).optional(), + disableCodexProjectInstructions: z.literal(true).optional(), managedProfile: z.record(z.string(), z.unknown()).optional(), agentCoreProfile: z.record(z.string(), z.unknown()).optional(), }).strict(), approvalPolicy: z.enum(["never", "on-request", "untrusted"]), readOnly: z.boolean(), diff --git a/packages/plugins/sdk/tests/environment-tasks.test.ts b/packages/plugins/sdk/tests/environment-tasks.test.ts index 9d47c7c7ea7..47f368f909d 100644 --- a/packages/plugins/sdk/tests/environment-tasks.test.ts +++ b/packages/plugins/sdk/tests/environment-tasks.test.ts @@ -46,6 +46,18 @@ describe("task hosted search restriction", () => { }); }); +describe("Codex probe project-instruction policy", () => { + it("preserves explicit suppression alongside the hosted-search restriction", () => { + const operation = { ...prepare, requirements: { ...prepare.requirements, + settings: { disableCodexProjectInstructions: true, disableCodexWebSearch: true } } }; + expect(environmentTaskOperationSchema.parse(operation)).toEqual(operation); + }); + it.each([false, null, 0, "true"])("rejects an invalid suppression value %s", disableCodexProjectInstructions => { + expect(environmentTaskOperationSchema.safeParse({ ...prepare, requirements: { ...prepare.requirements, + settings: { disableCodexProjectInstructions } } }).success).toBe(false); + }); +}); + describe("implementation-independent GitHub credential broker", () => { it("accepts capability data without an executable, script, or filesystem path", () => { const operation = { ...prepare, githubBroker: { url: "https://core.example.test/api", token: "runtime-capability" } }; diff --git a/packages/shared/src/validators/issue.test.ts b/packages/shared/src/validators/issue.test.ts index afe1a37f4b5..14009bda483 100644 --- a/packages/shared/src/validators/issue.test.ts +++ b/packages/shared/src/validators/issue.test.ts @@ -16,6 +16,13 @@ import { import { createAgentSchema } from "./agent.js"; describe("issue validators", () => { + it.each(["BASH_ENV", "ZDOTDIR", "GH_CONFIG_DIR", "PAPERCLIP_GITHUB_LAUNCHER_DIR"])("rejects task-owned managed shell selector %s", key => { + for (const name of [key, key.toLowerCase()]) { + const overrides = {adapterConfig:{env:{[name]:"/workspace/startup"}}}; + expect(createIssueSchema.safeParse({title:"Task",assigneeAdapterOverrides:overrides}).success).toBe(false); + expect(updateIssueSchema.safeParse({assigneeAdapterOverrides:overrides}).success).toBe(false); + } + }); it("retains an optional nonnegative integer version for prepared-task promotion", () => { expect(updateIssueSchema.parse({ status: "todo", expectedStatusVersion: 0 }).expectedStatusVersion).toBe(0); expect(updateIssueSchema.parse({}).expectedStatusVersion).toBeUndefined(); diff --git a/packages/shared/src/validators/issue.ts b/packages/shared/src/validators/issue.ts index 9d99e246a58..6146fe1d2b2 100644 --- a/packages/shared/src/validators/issue.ts +++ b/packages/shared/src/validators/issue.ts @@ -309,6 +309,7 @@ export const issueAssigneeAdapterOverridesSchema = z adapterConfig: z.record(z.string(), z.unknown()) .refine(config => !Object.prototype.hasOwnProperty.call(config, "homeMode"), "homeMode must be set by an operator on the agent, not on a task") .refine(config => !config.env || typeof config.env !== "object" || !Object.keys(config.env).some(key => key.toUpperCase() === "PAPERCLIP_RUNNER_PREPARED_HARNESS_HOME"), "Prepared harness homes must be provisioned by the execution environment, not on a task") + .refine(config => !config.env || typeof config.env !== "object" || !Object.keys(config.env).some(key => ["BASH_ENV", "ZDOTDIR", "GH_CONFIG_DIR", "PAPERCLIP_GITHUB_LAUNCHER_DIR"].includes(key.toUpperCase())), "Managed shell helpers must be provisioned by the execution environment, not on a task") .optional(), useProjectWorkspace: z.boolean().optional(), }) diff --git a/scripts/pi-public-install-probe.mjs b/scripts/pi-public-install-probe.mjs index f3b26cf97c9..2eaf6117de1 100644 --- a/scripts/pi-public-install-probe.mjs +++ b/scripts/pi-public-install-probe.mjs @@ -52,8 +52,8 @@ assert.equal(JSON.parse(await readFile(join(server, 'package.json'), 'utf8')).na const { resolvePaperclipRunnerBinary } = await import(pathToFileURL(join(server, 'dist/services/native-runtime/native-codex-runner.js'))); const { createCapabilityRunnerdCodexTransport } = await import(pathToFileURL(join(server, 'dist/vendor/paperclip-runner/live/runnerd-codex-transport.js'))); const { QUALIFIED_ACPX_PROFILES } = await import(pathToFileURL(join(server, 'dist/vendor/paperclip-runner/drivers/acpx/qualified-profiles.js'))); -assert.equal(QUALIFIED_ACPX_PROFILES.pi.agentProfileVersion, 35); -assert.equal(QUALIFIED_ACPX_PROFILES.pi.commandDigest, 'sha256:9473de6ef4baf2bd82fe8b6757239bdc160960b0a78ae6f47fa9e0996544226c'); +assert.equal(QUALIFIED_ACPX_PROFILES.pi.agentProfileVersion, 36); +assert.equal(QUALIFIED_ACPX_PROFILES.pi.commandDigest, 'sha256:faafb66c09c1efc7eb4eda6baaa5813c975e8a992f9cd333687695aa5522d7dc'); assert.equal(Object.hasOwn(QUALIFIED_ACPX_PROFILES.pi, 'qualificationModel'), false); assert.equal(Object.hasOwn(QUALIFIED_ACPX_PROFILES.pi, 'reportedModelId'), false); const daemon = resolvePaperclipRunnerBinary(); diff --git a/server/src/__tests__/heartbeat-project-env.test.ts b/server/src/__tests__/heartbeat-project-env.test.ts index afd79b92263..9cd21e8d67e 100644 --- a/server/src/__tests__/heartbeat-project-env.test.ts +++ b/server/src/__tests__/heartbeat-project-env.test.ts @@ -21,6 +21,19 @@ import type { AuthorizationActor, AuthorizationDecision } from "../services/auth import { resolveManagedProjectWorkspaceDir } from "../home-paths.ts"; describe("resolveExecutionRunAdapterConfig", () => { + it("strips stored task shell selectors before secret resolution and provider projection", async () => { + const unsafe = {BASH_ENV:"/workspace/hook.sh",ZDOTDIR:"/workspace",GH_CONFIG_DIR:"/workspace/gh",PAPERCLIP_GITHUB_LAUNCHER_DIR:"/workspace/forged"}; + const resolveAdapterConfigForRuntime = vi.fn(async (_companyId, config) => ({config,secretKeys:new Set(),manifest:[]})); + const result = await resolveExecutionRunAdapterConfig({companyId:"company-1",adapterType:"paperclip_runner", + executionRunConfig:{env:{...unsafe,CUSTOM_VALUE:"allowed"}}, + secretsSvc:{resolveAdapterConfigForRuntime} as any}); + expect(resolveAdapterConfigForRuntime.mock.calls[0]?.[1]).toMatchObject({env:{CUSTOM_VALUE:"allowed"}}); + for (const key of Object.keys(unsafe)) { + expect(result.resolvedConfig.env).not.toHaveProperty(key); + expect(result.configuredTaskEnvironment).not.toHaveProperty(key); + expect((resolveAdapterConfigForRuntime.mock.calls[0]?.[1] as {env:object}).env).not.toHaveProperty(key); + } + }); it.each(["many variables", "large value"])("keeps legacy adapter configuration compatible with %s", async kind => { const env = kind === "many variables" ? Object.fromEntries(Array.from({ length: 129 }, (_, index) => [`CUSTOM_${index}`, "value"])) diff --git a/server/src/__tests__/workspace-runtime.test.ts b/server/src/__tests__/workspace-runtime.test.ts index d870f42b295..68a547a6f7d 100644 --- a/server/src/__tests__/workspace-runtime.test.ts +++ b/server/src/__tests__/workspace-runtime.test.ts @@ -169,7 +169,7 @@ async function writeRegisteredSourceConfig(baseCwd: string, instanceId = "source async function createTempRepo(defaultBranch = "main") { const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-worktree-repo-")); - await runGit(repoRoot, ["init"]); + await runGit(repoRoot, ["init", "--initial-branch", defaultBranch]); await runGit(repoRoot, ["config", "user.email", "paperclip@example.com"]); await runGit(repoRoot, ["config", "user.name", "Paperclip Test"]); await fs.writeFile(path.join(repoRoot, "README.md"), "hello\n", "utf8"); diff --git a/server/src/services/heartbeat/run-preparation.ts b/server/src/services/heartbeat/run-preparation.ts index c251379d9fd..2b8c3f5f701 100644 --- a/server/src/services/heartbeat/run-preparation.ts +++ b/server/src/services/heartbeat/run-preparation.ts @@ -227,6 +227,7 @@ const LOW_TRUST_SENSITIVE_ENV_KEY_RE = // 3. Any other PAPERCLIP_*-named binding is user data and flows through to // the run env like any non-prefixed binding. const FORBIDDEN_ENV_BINDING_KEYS = new Set([ + "BASH_ENV", "ZDOTDIR", "GH_CONFIG_DIR", CONFIGURED_ENVIRONMENT_KEYS, "PAPERCLIP_AGENT_KEY_ID", "PAPERCLIP_AGENT_PUBLIC_KEY", "PAPERCLIP_AGENT_PRIVATE_KEY", "PAPERCLIP_RUNNER_NETWORK_ACCESS",