Files
PaperClipAI/tests/runner-e2e/exe-shared-agents.ts
DottaandPaperclip 056703424a Add experimental durable exe.dev agent environments
Keep one VM per environment with independent lease cgroups, persistent workspaces, immutable runner images, and an explicit experimental gate. Add live compatibility, recovery, browser-preview, and stress campaigns.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-17 09:47:20 -05:00

56 lines
4.3 KiB
TypeScript

import assert from "node:assert/strict";
import { expect, type Page } from "@playwright/test";
import { RunnerApi, pollUntil } from "./api.js";
import { runnerProfiles, runnerTasks } from "./catalog.js";
import type { LiveFixtureValues } from "./live-fixtures.js";
/** Real native/native and native/legacy overlap on the same durable VM. */
export async function verifyExeSharedAgents(input: { api: RunnerApi; page: Page; fixtures: LiveFixtureValues; nonce: string; workspacePath: string }) {
const { api, fixtures } = input;
const task = runnerTasks.find((entry) => entry.id === "message-marker")!;
const agents = await Promise.all(["runner-codex", "runner-codex", "legacy-codex"].map(async (id, i) => {
const profile = runnerProfiles.find((entry) => entry.id === id)!;
const nonce = `${input.nonce}-shared-${i}`;
const agent = await api.post<{ id: string }>(`/api/companies/${fixtures.company.id}/agents`, profile.buildAgent({
environmentId: fixtures.environment.id, environmentFixtureId: "exe-dev", workspacePath: input.workspacePath,
secretRefs: fixtures.secretRefs, executionId: nonce,
}));
return { id: agent.id, profile: id, nonce };
}));
type Run = { id: string; status: string; startedAt: string; finishedAt: string; error?: string; contextSnapshot?: { issueId?: string; taskId?: string } };
const issueRuns = async (issueId: string, agentId: string) =>
(await api.get<Run[]>(`/api/companies/${fixtures.company.id}/heartbeat-runs?agentId=${agentId}&limit=20`))
.filter((run) => run.contextSnapshot?.issueId === issueId || run.contextSnapshot?.taskId === issueId);
const issues = await Promise.all(agents.map(async (agent) => ({ agent, issue: await api.post<{ id: string; identifier: string }>(`/api/companies/${fixtures.company.id}/issues`, {
title: task.buildTitle(agent.nonce), description: task.buildPrompt(agent.nonce), status: "todo", assigneeAgentId: agent.id,
}) })));
try {
const results = await Promise.all(issues.map(async ({ agent, issue }) => {
const result = await pollUntil({ label: `shared ${agent.profile} task`, deadlineAt: Date.now() + 10 * 60_000, intervalMs: 1000,
load: async () => ({ issue: await api.get<{ status: string }>(`/api/issues/${issue.id}`), runs: await issueRuns(issue.id, agent.id) }),
accept: (value) => value.issue.status === "done" && value.runs.some((run) => run.status === "succeeded") && value.runs.every((run) => ["succeeded", "failed", "cancelled", "timed_out"].includes(run.status)),
reject: (value) => value.runs.some((run) => ["failed", "timed_out"].includes(run.status)) ? `Shared agent failed: ${value.runs.map((run) => run.error ?? run.status).join("; ")}` : undefined,
});
return { agentId: agent.id, profile: agent.profile, issueId: issue.id, runs: result.runs };
}));
const firstRuns = results.map((result) => result.runs.find((run) => run.status === "succeeded")!);
assert.ok(Math.max(...firstRuns.map((run) => Date.parse(run.startedAt))) < Math.min(...firstRuns.map((run) => Date.parse(run.finishedAt))), "All three real agent executions overlapped");
const leases = await api.get<{ issueId: string; metadata: Record<string, unknown> }[]>(`/api/environments/${fixtures.environment.id}/leases`);
const shared = leases.filter((lease) => issues.some(({ issue }) => issue.id === lease.issueId));
assert.equal(new Set(shared.map((lease) => lease.issueId)).size, 3);
assert.equal(new Set(shared.map((lease) => lease.metadata.vmName)).size, 1);
assert.equal(new Set(shared.map((lease) => lease.metadata.remoteHome)).size, 3);
assert.equal(new Set(shared.map((lease) => lease.metadata.remoteCwd)).size, 3);
for (const { agent, issue } of issues) {
await input.page.goto(`${api.baseURL}/${fixtures.company.issuePrefix}/issues/${issue.identifier}`);
await expect(input.page.getByText(task.buildVisibleMarker(agent.nonce), { exact: true }).first()).toBeVisible({ timeout: 30000 });
}
return { results, vmName: shared[0].metadata.vmName, independentHomes: 3, independentWorkspaces: 3 };
} finally {
for (const { issue, agent } of issues) {
const runs = await issueRuns(issue.id, agent.id);
for (const run of runs) if (["queued", "running"].includes(run.status)) await api.post(`/api/heartbeat-runs/${run.id}/cancel`);
}
}
}