mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 02:26:12 +02:00
Seed continuation evidence in the realized agent workspace
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
45cfa58c4b
commit
3ad34bf3dc
3 files changed
+51
-7
No files matched your search
@@ -1,6 +1,6 @@
|
||||
import { expect, type Page } from "@playwright/test";
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { seedContinuationContext } from "./continuation-workspace.js";
|
||||
import { pollUntil, type RunnerApi } from "./api.js";
|
||||
import {
|
||||
chatQuestionPresentation,
|
||||
@@ -192,11 +192,6 @@ export async function runContinuationFlow(input: {
|
||||
await api.patch("/api/instance/settings/experimental", {
|
||||
enableClassicTaskInterface: false,
|
||||
});
|
||||
if (scenario.id === "untrusted-evidence")
|
||||
await writeFile(
|
||||
path.join(input.workspacePath, "context.txt"),
|
||||
scenario.context,
|
||||
);
|
||||
await createTaskThroughUi({
|
||||
page,
|
||||
issuePrefix: fixtures.company.issuePrefix!,
|
||||
@@ -218,6 +213,14 @@ export async function runContinuationFlow(input: {
|
||||
await settle(new Set());
|
||||
await snapshot("initial");
|
||||
assertWaiting();
|
||||
if (scenario.id === "untrusted-evidence") {
|
||||
const parentRun = runs.find((r) => r.contextSnapshot?.issueId === issue!.id);
|
||||
await seedContinuationContext({
|
||||
isolatedRoot: path.dirname(input.workspacePath),
|
||||
recordedCwd: parentRun?.contextSnapshot?.paperclipWorkspace?.cwd,
|
||||
body: scenario.context,
|
||||
});
|
||||
}
|
||||
if (scenario.id === "completed-action-resume") {
|
||||
await input.restart();
|
||||
await open();
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { realpath, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
/** Seed the realized agent workspace, which can differ from the harness cwd. */
|
||||
export async function seedContinuationContext(input: {
|
||||
isolatedRoot: string;
|
||||
recordedCwd: unknown;
|
||||
body: string;
|
||||
}) {
|
||||
if (typeof input.recordedCwd !== "string" || !path.isAbsolute(input.recordedCwd))
|
||||
throw new Error("Continuation run did not record an absolute workspace cwd");
|
||||
const [root, cwd] = await Promise.all([
|
||||
realpath(input.isolatedRoot),
|
||||
realpath(input.recordedCwd),
|
||||
]);
|
||||
const relative = path.relative(root, cwd);
|
||||
if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative))
|
||||
throw new Error("Continuation workspace escaped the isolated instance");
|
||||
const file = path.join(cwd, "context.txt");
|
||||
await writeFile(file, input.body, { flag: "wx" });
|
||||
return file;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
|
||||
import { mkdtemp, mkdir, writeFile, readFile, realpath, symlink, rm } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { seedContinuationContext } from "./continuation-workspace.js";
|
||||
import { packageEvidence } from "./evidence.js";
|
||||
import { continuationScreenshotFile } from "./continuation-cases.js";
|
||||
import { describe, expect, it } from "vitest";
|
||||
@@ -163,3 +164,21 @@ it("packages all continuation checkpoints using the shared evidence rules", asyn
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it("seeds the recorded agent home rather than the harness workspace", async () => {
|
||||
const root = await mkdtemp(path.join(os.tmpdir(), "continuation-cwd-"));
|
||||
try {
|
||||
const recordedCwd = path.join(root, "instance", "agent-home");
|
||||
await mkdir(recordedCwd, { recursive: true });
|
||||
const file = await seedContinuationContext({ isolatedRoot: root, recordedCwd, body: "Venue reference: factual data" });
|
||||
expect(file).toBe(path.join(await realpath(recordedCwd), "context.txt"));
|
||||
expect(await readFile(file, "utf8")).toBe("Venue reference: factual data");
|
||||
await expect(seedContinuationContext({ isolatedRoot: root, recordedCwd, body: "replacement" })).rejects.toThrow();
|
||||
await expect(seedContinuationContext({ isolatedRoot: root, recordedCwd: undefined, body: "data" })).rejects.toThrow("record an absolute");
|
||||
await symlink(os.tmpdir(), path.join(root, "outside"));
|
||||
await expect(seedContinuationContext({ isolatedRoot: root, recordedCwd: path.join(root, "outside"), body: "data" })).rejects.toThrow("escaped");
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user