mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-28 12:04:41 +01:00
Keep extended runner acceptance cases in the stacked feature PR
Preserve the original baseline suites on the backend branch while the full Boat feature branch retains all added runtime-context, transport, and SSH acceptance coverage. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
00d1d4ea61
commit
180bfdcb0a
6 files changed
-121
No files matched your search
@@ -210,29 +210,3 @@ describe("paperclip runner transport routing", () => {
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it("uses each computer owner's allocated port without changing the sandbox default", async () => {
|
||||
const { adapterExecutionTargetIsCommandBacked } = await import("./execution-target.js");
|
||||
const endpoint = ingress();
|
||||
const getRunnerIngressEndpoint = vi.fn(async () => endpoint);
|
||||
const base = {
|
||||
kind: "remote" as const, transport: "computer" as const, providerKey: "boat", remoteCwd: "/home/user/project",
|
||||
leaseId: "attempt-lease", listenerPort: 45101, effectiveCapabilities: capabilities, getRunnerIngressEndpoint,
|
||||
processRunner: { execute: async () => ({ exitCode: 0, signal: null, timedOut: false, stdout: "", stderr: "", pid: null, startedAt: new Date().toISOString() }) },
|
||||
resourceAuthority: { kind: "computer-owner" as const, computerId: "machine", ownerId: "owner-a", generation: 2 },
|
||||
fileAuthority: { kind: "remote-persistent" as const, placementId: "placement", root: "/home/user/project", agentHome: "/home/user/agent-a" },
|
||||
launch: async () => ({}), inspectProcess: async () => ({ running: true, claim: {} }),
|
||||
retainWarm: async () => {}, retire: async () => true, computerTool: { command: "cua-driver", args: ["mcp"] },
|
||||
};
|
||||
expect(adapterExecutionTargetIsCommandBacked(base)).toBe(true);
|
||||
for (const port of [45101, 45102]) {
|
||||
const resolved = await resolvePaperclipRunnerTransport({ target: { ...base, listenerPort: port }, runId: "run-1",
|
||||
localConnectUrl: "ws://localhost/unused", runnerIngressAuthorized: true });
|
||||
expect(resolved).toMatchObject({ mode: "provider_ingress", listenPort: port });
|
||||
expect(getRunnerIngressEndpoint).toHaveBeenLastCalledWith({ leaseId: "attempt-lease", port, path: "/api/runner/v1/connect/run-1" });
|
||||
}
|
||||
const { listenerPort, processRunner, resourceAuthority, fileAuthority, launch, inspectProcess, retainWarm, retire, computerTool, ...common } = base;
|
||||
const resolved = await resolvePaperclipRunnerTransport({ target: { ...common, transport: "sandbox" }, runId: "sandbox-run",
|
||||
localConnectUrl: "ws://localhost/unused", runnerIngressAuthorized: true });
|
||||
expect(resolved).toMatchObject({ mode: "provider_ingress", listenPort: 43127 });
|
||||
});
|
||||
@@ -6,7 +6,6 @@ import path from "node:path";
|
||||
import { afterAll, afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildSshSpawnTarget,
|
||||
createSshCommandManagedRuntimeRunner,
|
||||
buildSshEnvLabFixtureConfig,
|
||||
getSshEnvLabSupport,
|
||||
prepareWorkspaceForSshExecution,
|
||||
@@ -182,28 +181,6 @@ describe("ssh env-lab fixture", () => {
|
||||
// this drains it too instead of stranding a listener until the process exits.
|
||||
afterAll(drainFixtureTeardowns);
|
||||
|
||||
it.each([0, 7])("handles a closed SSH input pipe without losing exit diagnostics (exit %i)", async (exitCode) => {
|
||||
const rootDir = await createFixtureRootDir();
|
||||
const previousPath = process.env.PATH;
|
||||
await writeFile(path.join(rootDir, "ssh"), `#!/bin/sh
|
||||
exec 0<&-
|
||||
printf 'remote rejected upload' >&2
|
||||
sleep 0.05
|
||||
exit ${exitCode}
|
||||
`, { mode: 0o700 });
|
||||
process.env.PATH = `${rootDir}${path.delimiter}${previousPath ?? ""}`;
|
||||
try {
|
||||
const runner = createSshCommandManagedRuntimeRunner({ spec: UNREACHABLE_SSH_SPEC });
|
||||
const result = await runner.execute({ command: "cat", stdin: "x".repeat(8 * 1024 * 1024), timeoutMs: 5000 });
|
||||
expect(result.exitCode).toBe(exitCode === 0 ? null : exitCode);
|
||||
expect(result.stderr).toBe("remote rejected upload");
|
||||
expect(result.timedOut).toBe(false);
|
||||
} finally {
|
||||
if (previousPath === undefined) delete process.env.PATH;
|
||||
else process.env.PATH = previousPath;
|
||||
}
|
||||
});
|
||||
|
||||
it("starts an isolated sshd fixture and executes commands through it", async () => {
|
||||
const rootDir = await createFixtureRootDir();
|
||||
const statePath = path.join(rootDir, "state.json");
|
||||
|
||||
@@ -37,13 +37,6 @@ function savedContext(revision: string, text: string) {
|
||||
}
|
||||
|
||||
describe("persisted native execution prompts", () => {
|
||||
it("preserves an authenticated persistent home independently of instruction copies", () => {
|
||||
const context = { ...savedContext("saved", "Instructions"), persistentAgentHome: { rootPath: "/computer/agent" } };
|
||||
expect(parseNativeRuntimeContext(context).persistentAgentHome).toEqual({ rootPath: "/computer/agent" });
|
||||
expect(parseNativeRuntimeContext(context).instructions.workingCopy).toBeUndefined();
|
||||
expect(() => parseNativeRuntimeContext({ ...context, persistentAgentHome: { rootPath: "" } })).toThrow();
|
||||
expect(() => parseNativeRuntimeContext({ ...context, persistentAgentHome: { rootPath: "/computer/agent", untrusted: true } })).toThrow();
|
||||
});
|
||||
it.each([
|
||||
["snapshot-before-upgrade", "Saved instructions from before the upgrade."],
|
||||
["future-prompt-fixture", " Saved instructions absent from this release.\n"],
|
||||
|
||||
@@ -18,19 +18,6 @@ function fixture() {
|
||||
}
|
||||
|
||||
describe("registered ACP agent files", () => {
|
||||
it("binds a persistent computer home without inventing an instruction copy", () => {
|
||||
const { root, parent, context } = fixture();
|
||||
delete context.instructions.workingCopy;
|
||||
context.persistentAgentHome = { rootPath: root };
|
||||
const nestedRuntime = join(root, ".paperclip-runtime"); mkdirSync(nestedRuntime);
|
||||
const binding = bindAcpxAgentFiles(context, [nestedRuntime])!;
|
||||
expect(binding.root.endsWith("registered-run-copy")).toBe(true);
|
||||
for (const protectedRoot of [parent, root]) {
|
||||
expect(() => bindAcpxAgentFiles(context, [protectedRoot])).toThrow("overlaps");
|
||||
}
|
||||
renameSync(root, `${root}-old`); mkdirSync(root);
|
||||
expect(() => binding.assertHeld()).toThrow("changed");
|
||||
});
|
||||
it("does not grant absent or legacy instruction copies", () => {
|
||||
expect(bindAcpxAgentFiles(null, [])).toBeNull();
|
||||
const { context } = fixture();
|
||||
|
||||
@@ -901,34 +901,6 @@ describe("ACPX runtime host", () => {
|
||||
expect(fixture.commandClose).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("gives Claude shells only the registered persistent home while keeping provider HOME isolated", async () => {
|
||||
const fixture = await hostFixture();
|
||||
const home = await realpath(await mkdtemp(join(tmpdir(), "paperclip-computer-agent-home-")));
|
||||
temporaryDirectories.push(home);
|
||||
const context: NativeRuntimeContextSnapshot = {
|
||||
prompt: { revision: PAPERCLIP_EXECUTION_PROMPT_REVISION, text: PAPERCLIP_EXECUTION_PROMPT, digest: nativeRuntimePromptDigest() },
|
||||
instructions: { entryPath: "AGENTS.md", bundle: { schema: NATIVE_RUNTIME_ASSET_SCHEMA, digest: "0".repeat(64), manifestDigest: "0".repeat(64), rootPath: home, fileCount: 0, totalBytes: 0 } },
|
||||
persistentAgentHome: { rootPath: home }, skills: [],
|
||||
mcp: { assignmentSetId: "none", digest: "0".repeat(64), bindingId: null }, aggregateDigest: "",
|
||||
};
|
||||
context.aggregateDigest = canonicalNativeRuntimeContextDigest(context);
|
||||
let observed: { home: string; agentHome: string } | undefined;
|
||||
const host = await AcpxRuntimeHost.open({ ...fixture.options, agent: "claude", model: "claude-sonnet-5",
|
||||
environment: { AGENT_HOME: "/ambient/untrusted" }, runtimeContext: context,
|
||||
}, fixture.dependencies({ openRuntime: async launch => {
|
||||
observed = JSON.parse(execFileSync(process.execPath, ["-e", 'console.log(JSON.stringify({home:process.env.HOME,agentHome:process.env.AGENT_HOME}))'], {
|
||||
env: launch.launchEnvironment, encoding: "utf8", timeout: 5_000,
|
||||
}));
|
||||
return runtimePort({ getStatus: async () => ({ models: { currentModelId: "claude-sonnet-5" } }) });
|
||||
} }));
|
||||
try {
|
||||
expect(observed?.agentHome).toBe(home);
|
||||
expect(observed?.home).not.toBe(home);
|
||||
expect(observed?.home).toContain("home");
|
||||
expect(context.instructions.workingCopy).toBeUndefined();
|
||||
} finally { await host.close({ reason: "persistent home verified" }); }
|
||||
});
|
||||
|
||||
it.each(["cursor", "copilot", "pi"] as const)("binds %s agent files from each registered run copy, never ambient roots", async (agent) => {
|
||||
const fixture = await hostFixture();
|
||||
const copies = await mkdtemp(join(tmpdir(), "paperclip-agent-copies-"));
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { createRunnerdCodexAppServerArgs } from "../live/runnerd-codex-transport.js";
|
||||
import {
|
||||
chmod,
|
||||
lstat,
|
||||
@@ -28,7 +27,6 @@ import { nativeMcpLaunchBinding } from "./native-mcp.js";
|
||||
import {
|
||||
materializeNativeRuntimeSkills,
|
||||
prepareIsolatedCodexHome,
|
||||
releaseMaterializedNativeRuntimeSkills,
|
||||
} from "./runtime-context-materializer.js";
|
||||
|
||||
const roots: string[] = [];
|
||||
@@ -95,19 +93,6 @@ function context(
|
||||
}
|
||||
|
||||
describe("runtime context materialization", () => {
|
||||
it("installs only the trusted environment computer binding and removes it on refresh without that capability", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "paperclip-computer-context-"));
|
||||
roots.push(root);
|
||||
const codexHome = join(root, "codex-home");
|
||||
await prepareIsolatedCodexHome({ context: null, codexHome,
|
||||
computerTool: { command: "/opt/ascii/cua-driver/cua-driver", args: ["mcp", "--socket", "/run/ascii-cua/driver.sock"] } });
|
||||
const config = await readFile(join(codexHome, "config.toml"), "utf8");
|
||||
expect(config).toContain("[mcp_servers.paperclip_computer]");
|
||||
expect(config).toContain('command = "/opt/ascii/cua-driver/cua-driver"');
|
||||
await releaseMaterializedNativeRuntimeSkills(join(codexHome, "skills"));
|
||||
await prepareIsolatedCodexHome({ context: null, codexHome });
|
||||
expect(await readFile(join(codexHome, "config.toml"), "utf8")).not.toContain("paperclip_computer");
|
||||
});
|
||||
it("restores the legacy working-copy contract without changing its digest or adding new fields", () => {
|
||||
const old = context("/skills", "/instructions");
|
||||
old.instructions.workingCopy = { rootPath: "/old-run/copy", entryPath: "AGENTS.md" };
|
||||
@@ -518,12 +503,3 @@ command = "untrusted-command"
|
||||
await expect(stat(join(symlinkCodexHome, "auth.json"))).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it("exposes a trusted persistent personal directory without instruction files", () => {
|
||||
const options = { environment: { AGENT_HOME: "/untrusted", HOME: "/ambient" }, codexHome: "/private-provider" };
|
||||
const supplied = createRunnerdCodexAppServerArgs({ ...options, persistentAgentHome: "/personal/agent" }).join("\n");
|
||||
expect(supplied).toContain('AGENT_HOME="/personal/agent"');
|
||||
expect(supplied).toContain('"/private-provider"="none"');
|
||||
expect(supplied).not.toContain('AGENT_HOME="/untrusted"');
|
||||
expect(createRunnerdCodexAppServerArgs(options).join("\n")).not.toContain("AGENT_HOME");
|
||||
});
|
||||
Reference in new issue
Block a user