mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-14 17:40:37 +02:00
Move bootstrap and instruction acceptance coverage to stacked PR
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
ab5bb8552a
commit
26d404cbfe
2 files changed
+4
-77
No files matched your search
@@ -326,7 +326,7 @@ describe("executeNativeSession recovery", () => {
|
||||
const completeRun = vi.fn(async () => {});
|
||||
try {
|
||||
const execution = executeNativeSession({
|
||||
input, turnTimeoutMs, bootstrapTimeoutMs: 5,
|
||||
input, turnTimeoutMs,
|
||||
backend: {
|
||||
async descriptor() { return { kind: "mock", name: "long-lived", version: "1", capabilities }; },
|
||||
async openSession() { return session; },
|
||||
@@ -2256,18 +2256,15 @@ describe("executeNativeSession recovery", () => {
|
||||
runnerInstanceId: "runner-recovery",
|
||||
controlPlaneInstanceId: "control-recovery",
|
||||
timeoutMs: 5,
|
||||
bootstrapTimeoutMs: 10,
|
||||
});
|
||||
const execution = execute();
|
||||
const rejection = expect(execution).rejects.toThrow(
|
||||
"native session bootstrap timed out after 10ms",
|
||||
"native session bootstrap timed out after 5ms",
|
||||
);
|
||||
|
||||
await bootstrapStarted;
|
||||
expect(bootstrapSignal?.aborted).toBe(false);
|
||||
await vi.advanceTimersByTimeAsync(5);
|
||||
expect(bootstrapSignal?.aborted).toBe(false);
|
||||
await vi.advanceTimersByTimeAsync(5);
|
||||
await rejection;
|
||||
expect(bootstrapSignal?.aborted).toBe(true);
|
||||
expect(openRun).not.toHaveBeenCalled();
|
||||
@@ -2662,7 +2659,6 @@ describe("executeNativeSession recovery", () => {
|
||||
runnerInstanceId: "runner-recovery",
|
||||
controlPlaneInstanceId: "control-recovery",
|
||||
timeoutMs: 10,
|
||||
bootstrapTimeoutMs: 100,
|
||||
keepSessionOpen: true,
|
||||
onSession: (current) => retainedSessions.push(current),
|
||||
});
|
||||
@@ -5517,7 +5513,6 @@ describe("executeNativeSession recovery", () => {
|
||||
controlPlaneInstanceId: "control-recovery",
|
||||
persistedSession: structuredClone(checkpoint),
|
||||
timeoutMs: 5,
|
||||
bootstrapTimeoutMs: 100,
|
||||
onSession,
|
||||
});
|
||||
const rejection = expect(execution).rejects.toThrow(
|
||||
@@ -5653,18 +5648,15 @@ describe("executeNativeSession recovery", () => {
|
||||
controlPlaneInstanceId: "control-recovery",
|
||||
persistedSession: structuredClone(checkpoint),
|
||||
timeoutMs: 5,
|
||||
bootstrapTimeoutMs: 10,
|
||||
onSession,
|
||||
});
|
||||
const rejection = expect(execution).rejects.toThrow(
|
||||
"native session replacement bootstrap timed out after 10ms",
|
||||
"native session replacement bootstrap timed out after 5ms",
|
||||
);
|
||||
|
||||
await replacementStarted;
|
||||
expect(replacementSignal?.aborted).toBe(false);
|
||||
await vi.advanceTimersByTimeAsync(5);
|
||||
expect(replacementSignal?.aborted).toBe(false);
|
||||
await vi.advanceTimersByTimeAsync(5);
|
||||
await rejection;
|
||||
expect(replacementSignal?.aborted).toBe(true);
|
||||
expect(openRun).not.toHaveBeenCalled();
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import * as fileStore from "../services/agent-file-store.js";
|
||||
import * as persistentFiles from "../services/persistent-agent-files.js";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { agentInstructionsService } from "../services/agent-instructions.js";
|
||||
|
||||
type TestAgent = {
|
||||
@@ -39,7 +37,6 @@ describe("agent instructions service", () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
vi.restoreAllMocks();
|
||||
if (originalPaperclipHome === undefined) delete process.env.PAPERCLIP_HOME;
|
||||
else process.env.PAPERCLIP_HOME = originalPaperclipHome;
|
||||
if (originalPaperclipInstanceId === undefined) delete process.env.PAPERCLIP_INSTANCE_ID;
|
||||
@@ -62,68 +59,6 @@ describe("agent instructions service", () => {
|
||||
if (entry === "../invalid") expect(bundle.warnings.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it.each(["explicit", "legacy"])("keeps %s external instructions on their configured root after selecting Boat", async (configuration) => {
|
||||
const root = await makeTempDir("boat-external-instructions-"); cleanupDirs.add(root);
|
||||
await fs.writeFile(path.join(root, "AGENTS.md"), "external authority");
|
||||
await fs.writeFile(path.join(root, "NEXT.md"), "next external entry");
|
||||
const agent = { ...makeAgent(configuration === "explicit"
|
||||
? { instructionsBundleMode: "external", instructionsRootPath: root, instructionsEntryFile: "AGENTS.md" }
|
||||
: { instructionsFilePath: path.join(root, "AGENTS.md") }), defaultEnvironmentId: "boat-environment" };
|
||||
const remote = vi.spyOn(persistentFiles, "persistentAgentFiles").mockResolvedValue({ root: "/remote/personal" } as never);
|
||||
const db = { transaction: vi.fn(async () => { throw new Error("Must not adopt external instructions"); }) };
|
||||
const svc = agentInstructionsService(db as never);
|
||||
expect((await svc.getBundle(agent)).rootPath).toBe(root);
|
||||
expect((await svc.readFile(agent, "AGENTS.md")).content).toBe("external authority");
|
||||
expect((await svc.exportFiles(agent)).files["AGENTS.md"]).toBe("external authority");
|
||||
const updated = await svc.updateBundle(agent, { entryFile: "NEXT.md" });
|
||||
expect(updated.bundle.mode).toBe("external");
|
||||
expect(updated.bundle.rootPath).toBe(root);
|
||||
expect(updated.bundle.entryFile).toBe("NEXT.md");
|
||||
expect(remote).not.toHaveBeenCalled();
|
||||
expect(db.transaction).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("excludes only home-level runtime files from remote listing and export", async () => {
|
||||
const agent = makeAgent({ instructionsBundleMode: "managed" });
|
||||
const list = vi.fn(async (relative = "") => {
|
||||
if (relative === ".paperclip-runtime") throw new Error("Must not enumerate provider packs or session state");
|
||||
if (!relative) return [{ name: "AGENTS.md", kind: "file", size: 12 }, { name: ".paperclip-runtime", kind: "directory", size: 0 }, { name: "notes", kind: "directory", size: 0 }];
|
||||
if (relative === "notes") return [{ name: ".paperclip-runtime", kind: "directory", size: 0 }];
|
||||
return [{ name: "user.md", kind: "file", size: 12 }];
|
||||
});
|
||||
const readBytes = vi.fn(async () => ({ bytes: Buffer.from("instructions"), sha256: "hash" }));
|
||||
vi.spyOn(persistentFiles, "persistentAgentFiles").mockResolvedValue({ root: "/remote/home", list, readBytes } as never);
|
||||
vi.spyOn(persistentFiles, "seedPersistentAgentHome").mockResolvedValue(undefined);
|
||||
vi.spyOn(fileStore, "adoptAgentFiles").mockResolvedValue("/controller/home");
|
||||
const db = { transaction: async (fn: (tx: unknown) => Promise<unknown>) => fn({ select: () => ({ from: () => ({ where: async () => [agent] }) }) }) };
|
||||
const svc = agentInstructionsService(db as never);
|
||||
expect((await svc.getBundle(agent)).files.map(file => file.path)).toEqual(["AGENTS.md", "notes/.paperclip-runtime/user.md"]);
|
||||
expect((await svc.exportFiles(agent)).files).toEqual({ "AGENTS.md": "instructions", "notes/.paperclip-runtime/user.md": "instructions" });
|
||||
expect(list).not.toHaveBeenCalledWith(".paperclip-runtime");
|
||||
expect(readBytes).not.toHaveBeenCalledWith(expect.stringMatching(/^\.paperclip-runtime\//));
|
||||
});
|
||||
|
||||
it("previews an explicit external-to-managed migration before adopting remote personal files", async () => {
|
||||
const root = await makeTempDir("boat-instructions-migration-"); cleanupDirs.add(root);
|
||||
await fs.writeFile(path.join(root, "AGENTS.md"), "external migration source");
|
||||
const agent = makeAgent({ instructionsBundleMode: "external", instructionsRootPath: root, instructionsEntryFile: "AGENTS.md" });
|
||||
const seedBytes = vi.fn();
|
||||
vi.spyOn(persistentFiles, "persistentAgentFiles").mockResolvedValue({ root: "/remote/personal", seedBytes } as never);
|
||||
const db = {
|
||||
select: () => ({ from: () => ({ where: async () => [] }) }),
|
||||
transaction: async (fn: (tx: unknown) => Promise<unknown>) => fn({
|
||||
select: () => ({ from: () => ({ where: async () => [agent] }) }),
|
||||
}),
|
||||
};
|
||||
const svc = agentInstructionsService(db as never);
|
||||
const updated = await svc.updateBundle(agent, { mode: "managed" });
|
||||
expect(updated.bundle.mode).toBe("managed");
|
||||
expect(updated.bundle.rootPath).toBe(updated.bundle.managedRootPath);
|
||||
expect(await fs.readFile(path.join(updated.bundle.managedRootPath, "AGENTS.md"), "utf8")).toBe("external migration source");
|
||||
expect(seedBytes).not.toHaveBeenCalled();
|
||||
expect(await fs.readFile(path.join(root, "AGENTS.md"), "utf8")).toBe("external migration source");
|
||||
});
|
||||
|
||||
it("lists external bundles containing large binary assets", async () => {
|
||||
const root = await makeTempDir("external-large-assets-"); cleanupDirs.add(root);
|
||||
await fs.writeFile(path.join(root, "AGENTS.md"), "external instructions");
|
||||
|
||||
Reference in new issue
Block a user