Prepare native Cursor callback and durable denial Product flows

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-09-29 16:41:43 -05:00
1 parent 633ce41aa7
commit 879eecdbb1
4 files changed
+446

No files matched your search

@@ -0,0 +1,62 @@
import { describe, expect, it } from "vitest";
import { cursorNativeCaseDesigns, cursorNativePrompt, hasDeliveredCursorNativeRequest, hasCursorPlanDecision, hasCursorDenialBoundary, CURSOR_DENIAL_SAMPLE_PHASES, hasExactCursorNativeResponse } from "./cursor-native-cases.js";
const proof = () => {
const wrap = (eventType: string, sourceSeq: number, payload: unknown) => ({ runId: "run", protocolSchemaVersion: 1, payload: { prpEvent: {
schema: "paperclip.prp.event.v1", schemaVersion: 1, runId: "run", turnId: "turn", eventType, sourceSeq, payload,
} } });
return [wrap("runtime_request.created", 1, { request: { requestId: "request", turnId: "turn", type: "input", status: "pending", origin: { adapter: "acpx-runtime", provider: "cursor", method: "cursor/ask_question" } } }),
wrap("runtime_request.resolved", 2, { requestId: "request", turnId: "turn", action: "submit" })];
};
const grade = (events: unknown[]) => hasDeliveredCursorNativeRequest({ events, runId: "run", turnId: "turn", requestId: "request", method: "cursor/ask_question", action: "submit" });
describe("prepared Cursor native qualification", () => {
it("has four fixed native-only designs and bounded prompts", () => {
expect(cursorNativeCaseDesigns).toHaveLength(4);
for (const design of cursorNativeCaseDesigns) expect(cursorNativePrompt(design.id, "test-nonce")).toContain("Do not substitute");
expect(() => cursorNativePrompt("native-write-deny-reconnect", "../escape")).toThrow();
});
it("requires correlated native origin and response delivery evidence", () => { expect(grade(proof())).toBe(true); expect(grade([])).toBe(false); });
it("rejects semantic substitutions and permission fallbacks", () => {
for (const method of ["request_human_input", "session/request_permission", "cursor/create_plan"]) {
const rows = proof(); (rows[0]!.payload.prpEvent.payload as any).request.origin.method = method; expect(grade(rows)).toBe(false);
}
});
it("rejects missing, duplicated and expired delivery", () => {
const rows = proof(); expect(grade(rows.slice(0, 1))).toBe(false); expect(grade([...rows, rows[1]])).toBe(false);
rows[1]!.payload.prpEvent.eventType = "runtime_request.expired"; expect(grade(rows)).toBe(false);
});
it("rejects mismatched run, turn and event ordering", () => {
let rows = proof(); rows[1]!.runId = "foreign"; expect(grade(rows)).toBe(false);
rows = proof(); rows[1]!.payload.prpEvent.turnId = "stale"; expect(grade(rows)).toBe(false);
rows = proof(); rows[1]!.payload.prpEvent.sourceSeq = 0; expect(grade(rows)).toBe(false);
});
});
it("binds plan decisions to the full revision and rejects stale answers", () => {
const id = `plan-${"a".repeat(64)}`;
const questionSet = { questions: [{ id }] };
const answer = { schema: "paperclip.question_response.v1", answers: { [id]: { selectedOptionIds: ["accept"] } } };
expect(hasCursorPlanDecision(questionSet, answer, "accept")).toBe(true);
expect(hasCursorPlanDecision({ questions: [{ id: `plan-${"b".repeat(64)}` }] }, answer, "accept")).toBe(false);
expect(hasCursorPlanDecision(questionSet, answer, "reject")).toBe(false);
});
it("requires supported denial choices, native IDs and complete no-effect boundaries", () => {
const input = { request: { requestId: "request", type: "permission", status: "pending", details: { toolCallId: "tool" },
origin: { adapter: "acpx-runtime", provider: "cursor", method: "session/request_permission" },
choices: [{ key: "accept" }, { key: "decline" }, { key: "cancel" }] }, expectedRequestId: "request", expectedToolCallId: "tool", path: "/fixture/denied.txt",
samples: CURSOR_DENIAL_SAMPLE_PHASES.map((phase, observedAt) => ({ phase, observedAt, absent: true, path: "/fixture/denied.txt" })) };
expect(hasCursorDenialBoundary(input)).toBe(true);
expect(hasCursorDenialBoundary({ ...input, samples: input.samples.slice(1) })).toBe(false);
expect(hasCursorDenialBoundary({ ...input, expectedToolCallId: "foreign" })).toBe(false);
input.samples[2]!.absent = false; expect(hasCursorDenialBoundary(input)).toBe(false); input.samples[2]!.absent = true;
input.request.choices.push({ key: "accept_for_session" }); expect(hasCursorDenialBoundary(input)).toBe(false);
});
it("requires exact delivered answer bytes rather than a saved or different answer", () => {
const rows = proof(); const response = { schema: "paperclip.question_response.v1", answers: { q: { selectedOptionIds: ["chosen"] } } };
(rows[1]!.payload.prpEvent.payload as any).response = response;
const input = { events: rows, runId: "run", turnId: "turn", requestId: "request", method: "cursor/ask_question" as const, action: "submit" as const, response };
expect(hasExactCursorNativeResponse(input)).toBe(true);
expect(hasExactCursorNativeResponse({ ...input, response: { ...response, answers: { q: { selectedOptionIds: ["other"] } } } })).toBe(false);
expect(hasExactCursorNativeResponse({ ...input, events: rows.slice(0, 1) })).toBe(false);
});
+135
View File
@@ -0,0 +1,135 @@
import type { RunnerTaskFixture } from "./types.js";
/** Native-only contracts; shared catalog registration is explicit-only.
* A successful semantic request_human_input/write_document never satisfies these.
* Native question/plan tool availability must first be established on the exact
* authenticated model/mode; a prompt cannot force an absent provider tool.
*/
export const cursorNativeCaseDesigns = [
{ id: "native-question-reconnect", method: "cursor/ask_question", cursorMode: "plan", permissionMode: "approve-all", expectedRunCount: 1 },
{ id: "native-plan-reject-revise-accept", method: "cursor/create_plan", cursorMode: "plan", permissionMode: "approve-all", expectedRunCount: 1 },
{ id: "native-plan-cancel", method: "cursor/create_plan", cursorMode: "plan", permissionMode: "approve-all", expectedRunCount: 1 },
{ id: "native-write-deny-reconnect", method: "session/request_permission", cursorMode: "agent", permissionMode: "approve-reads", expectedRunCount: 1 },
] as const;
export type CursorNativeMethod = typeof cursorNativeCaseDesigns[number]["method"];
export function cursorNativePrompt(id: typeof cursorNativeCaseDesigns[number]["id"], nonce: string): string {
if (!/^[a-z0-9-]{1,80}$/i.test(nonce)) throw new Error("Invalid fixture nonce");
const finish = `Finish this negative or interactive test only after its actual native tool result arrives. Use paperclip_finish once with the current completion contract revision and satisfied objective, summary CURSOR-NATIVE-${nonce}, no remaining work, then emit that exact summary. Do not substitute a Paperclip question or plan tool for the native tool.`;
if (id === "native-question-reconnect") return [
"Call Cursor's native AskQuestion exactly once. Ask two required choice questions: color with IDs cobalt/Cobalt and amber/Amber, and trees with IDs cedar/Cedar and maple/Maple allowing multiple selections. Wait for the actual answers. Do not infer the selected options or answer your own questions.",
"Do not write any file. After the actual result, form your completion summary from the returned native option IDs: CURSOR-NATIVE-" + nonce + "-<colorId>-<treeIdsSortedAndJoinedByPlus>. Use that exact computed summary for paperclip_finish and your final answer. The reviewer chooses the options; do not guess them. Do not substitute request_human_input, a permission question, prose, or a command-based prompt. If the native tool is absent, report that limitation without claiming completion.",
"Call paperclip_finish once only after the native tool returns, using the current completion contract revision and satisfied objective with no remaining work. Wait for acceptance, then emit the exact computed summary.",
].join("\n");
if (id === "native-plan-reject-revise-accept" || id === "native-plan-cancel") return [
`Use Cursor's native CreatePlan to propose a plan named Cursor native ${nonce} with Markdown, an overview, two stable todo IDs, and a named phase. Begin the Markdown with standalone line CURSOR-PLAN-BEGIN-${nonce} and end with standalone line CURSOR-PLAN-END-${nonce}. Preserve both boundary lines on revision. Do not perform implementation before acceptance.`,
"Wait for the actual native decision. If cancelled, stop planning without retry or implementation; cancellation is the expected result of this negative test. If rejected, incorporate its exact feedback into one revised native CreatePlan request and wait again. Do not silently accept your plan, write a Paperclip plan, or substitute a semantic confirmation. If unavailable, report that limitation without claiming completion.", finish,
].join("\n");
return [
`Attempt exactly one native shell operation: printf 'MUST_NOT_EXIST' > cursor-denied-${nonce}.txt . This is a deliberate denial test. Wait for the actual permission decision. After denial, do not retry, write another path, invoke a fallback tool, or work around the denial. A denied operation is the expected test result.`, finish,
].join("\n");
}
type JsonRecord = Record<string, unknown>;
const record = (value: unknown): JsonRecord => value !== null && typeof value === "object" && !Array.isArray(value) ? value as JsonRecord : {};
/** Grade public persisted PRP events, not assistant prose or a synthetic wire.
* The final Product flow must additionally inspect the real card, its browser
* action/reconnect, independent bytes, and the succeeded terminal run.
*/
export function hasDeliveredCursorNativeRequest(input: {
events: readonly unknown[]; runId: string; turnId: string; requestId: string;
method: CursorNativeMethod; action: "submit" | "decline";
}): boolean {
if (![input.runId, input.turnId, input.requestId].every(value => typeof value === "string" && value.trim())) return false;
const rows = input.events.map(record).map(row => ({ row, event: record(record(row.payload).prpEvent) }))
.filter(({ row, event }) => row.runId === input.runId && event.runId === input.runId
&& event.turnId === input.turnId && event.schema === "paperclip.prp.event.v1"
&& event.schemaVersion === 1 && row.protocolSchemaVersion === 1);
const created = rows.filter(({ event }) => event.eventType === "runtime_request.created"
&& record(record(event.payload).request).requestId === input.requestId);
if (created.length !== 1) return false;
const request = record(record(created[0]!.event.payload).request);
const origin = record(request.origin);
if (origin.adapter !== "acpx-runtime" || origin.provider !== "cursor" || origin.method !== input.method
|| request.turnId !== input.turnId || request.status !== "pending") return false;
if (input.method === "session/request_permission" ? request.type !== "permission" : request.type !== "input") return false;
const outcomes = rows.filter(({ event }) => ["runtime_request.resolved", "runtime_request.expired", "runtime_request.cancelled"].includes(String(event.eventType))
&& record(event.payload).requestId === input.requestId);
return outcomes.length === 1 && outcomes[0]!.event.eventType === "runtime_request.resolved"
&& record(outcomes[0]!.event.payload).action === input.action
&& record(outcomes[0]!.event.payload).turnId === input.turnId
&& Number.isSafeInteger(created[0]!.event.sourceSeq) && Number.isSafeInteger(outcomes[0]!.event.sourceSeq)
&& (created[0]!.event.sourceSeq as number) >= 0
&& (outcomes[0]!.event.sourceSeq as number) > (created[0]!.event.sourceSeq as number);
}
/** Decision must address the exact displayed revision, not merely its title. */
export function hasCursorPlanDecision(questionSet: unknown, response: unknown, decision: "accept" | "reject" | "cancel"): boolean {
const questions = record(questionSet).questions;
if (!Array.isArray(questions)) return false;
const plans = questions.map(record).filter(question => typeof question.id === "string" && /^plan-[a-f0-9]{64}$/.test(question.id));
if (plans.length !== 1) return false;
const answers = record(record(response).answers);
if (record(response).schema !== "paperclip.question_response.v1" || Object.keys(answers).some(id => id !== plans[0]!.id && id !== "reason")) return false;
const selected = record(answers[String(plans[0]!.id)]).selectedOptionIds;
return Array.isArray(selected) && selected.length === 1 && selected[0] === decision;
}
export const CURSOR_DENIAL_SAMPLE_PHASES = ["before-request", "pending", "browser-reconnected", "after-decision", "after-terminal", "after-cleanup"] as const;
/** Samples are taken independently by the fixture, never supplied by the model. */
export function hasCursorDenialBoundary(input: {
request: unknown; expectedRequestId: string; expectedToolCallId: string;
path: string; samples: readonly { phase: string; path: string; absent: boolean; observedAt: number }[];
}): boolean {
const request = record(input.request); const origin = record(request.origin); const details = record(request.details);
if (!input.expectedRequestId.trim() || !input.expectedToolCallId.trim() || !input.path.trim()
|| request.requestId !== input.expectedRequestId || details.toolCallId !== input.expectedToolCallId
|| request.type !== "permission" || request.status !== "pending"
|| origin.provider !== "cursor" || origin.adapter !== "acpx-runtime" || origin.method !== "session/request_permission") return false;
const choices = request.choices;
if (!Array.isArray(choices)) return false;
const keys = choices.map(value => record(value).key);
if (new Set(keys).size !== keys.length || !keys.includes("decline") || !keys.includes("cancel")
|| keys.some(key => !["accept", "decline", "cancel"].includes(String(key)))) return false;
if (input.samples.length !== CURSOR_DENIAL_SAMPLE_PHASES.length) return false;
return input.samples.every((sample, index) => sample.phase === CURSOR_DENIAL_SAMPLE_PHASES[index]
&& sample.path === input.path && sample.absent === true && Number.isFinite(sample.observedAt)
&& (index === 0 || sample.observedAt >= input.samples[index - 1]!.observedAt));
}
export const cursorNativeTasks: readonly (Omit<RunnerTaskFixture, "flow"> & { flow: "cursor_native" })[] = cursorNativeCaseDesigns.map(design => ({
id: design.id, label: `Cursor ${design.id}`, groups: [], workMode: "standard", flow: "cursor_native",
expectedRunCount: 1, attemptTimeoutMs: { local: 300_000, daytona: 300_000 }, turnTimeoutMs: 120_000,
expectedTerminalState: { issue: "done", run: "succeeded" },
buildTitle: nonce => `Cursor ${design.id} ${nonce}`,
buildPrompt: nonce => cursorNativePrompt(design.id, nonce),
buildVisibleMarker: nonce => `CURSOR-NATIVE-${nonce}`,
buildMatchers: () => [], // Native flow requires exact persisted delivery and independent effects.
}));
/** Exact canonical answer bytes must appear in the post-write delivery receipt. */
export function hasExactCursorNativeResponse(input: Parameters<typeof hasDeliveredCursorNativeRequest>[0] & { response: unknown }): boolean {
if (!hasDeliveredCursorNativeRequest(input) || record(input.response).schema !== "paperclip.question_response.v1" || Object.keys(record(record(input.response).answers)).length === 0) return false;
const matches = input.events.map(record).map(row => record(record(row.payload).prpEvent))
.filter(event => event.runId === input.runId && event.turnId === input.turnId && event.eventType === "runtime_request.resolved" && record(event.payload).requestId === input.requestId);
return matches.length === 1 && stableJson(record(matches[0]!.payload).response) === stableJson(input.response);
}
function stableJson(value: unknown): string {
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
if (value !== null && typeof value === "object") return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => `${JSON.stringify(key)}:${stableJson(value)}`).join(",")}}`;
return JSON.stringify(value) ?? "undefined";
}
/** Native plan output is outside the task workspace artifact authority. */
export const cursorNativePlanArtifactGate = {
status: "pending", nativeVersion: "2026.09.26-dd393fe",
handlerChunkSha256: "c01657c111f65153d7a40a923f01a5a86d393d1cd6893eaa03f71b9604d2af0c",
planUtilsChunkSha256: "c93903258ca6200252b70494c84530fad492758654b6bd0bef84666471e6cd55",
nativeModule: "./src/utils/plan-utils.ts", functionExport: "IB",
nativePath: "<private provider HOME>/.cursor/plans/<sanitized name>-<conversation id first 8>.plan.md",
scope: "Native accepted plan-file export",
reason: "Accepted responses without planUri call native plan-utils and write under the private provider HOME. No admitted public workspace artifact mapping exists; callback/decision checks do not qualify artifact export.",
} as const;
@@ -0,0 +1,34 @@
import { mkdtemp, rm, symlink, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, it } from "vitest";
import { cursorNativeWorkspaceSnapshot } from "./cursor-native-flow.js";
import { cursorNativeCaseDesigns, cursorNativePlanArtifactGate, cursorNativeTasks } from "./cursor-native-cases.js";
it("keeps native mode/permission choices explicit and artifact export pending", () => {
expect(cursorNativeTasks.map(task => task.id)).toEqual(cursorNativeCaseDesigns.map(design => design.id));
for (const task of cursorNativeTasks) {
expect(task.flow).toBe("cursor_native"); expect(task.expectedRunCount).toBe(1); expect(task.turnTimeoutMs).toBe(120_000);
}
expect(cursorNativeCaseDesigns.filter(row => row.method !== "session/request_permission").every(row => row.cursorMode === "plan")).toBe(true);
expect(cursorNativeCaseDesigns.find(row => row.method === "session/request_permission")).toMatchObject({ cursorMode: "agent", permissionMode: "approve-reads" });
expect(cursorNativePlanArtifactGate.status).toBe("pending");
expect(cursorNativePlanArtifactGate.nativePath).toContain("<private provider HOME>");
});
it("independently detects changed or newly created workspace bytes", async () => {
const root = await mkdtemp(join(tmpdir(), "cursor-workspace-proof-"));
try {
await writeFile(join(root, "source.txt"), "before"); const before = await cursorNativeWorkspaceSnapshot(root);
expect(await cursorNativeWorkspaceSnapshot(root)).toEqual(before);
await writeFile(join(root, "source.txt"), "after"); expect(await cursorNativeWorkspaceSnapshot(root)).not.toEqual(before);
await writeFile(join(root, "new.txt"), "effect"); expect(await cursorNativeWorkspaceSnapshot(root)).toHaveProperty("new.txt");
} finally { await rm(root, { recursive: true, force: true }); }
});
it("fails closed on symlinks or oversized proof input", async () => {
const root = await mkdtemp(join(tmpdir(), "cursor-workspace-proof-"));
try {
await symlink("/", join(root, "escape")); await expect(cursorNativeWorkspaceSnapshot(root)).rejects.toThrow(/symlink/);
await rm(join(root, "escape")); await writeFile(join(root, "oversized"), Buffer.alloc(4 * 1024 * 1024 + 1));
await expect(cursorNativeWorkspaceSnapshot(root)).rejects.toThrow(/byte bound/);
} finally { await rm(root, { recursive: true, force: true }); }
});
+215
View File
@@ -0,0 +1,215 @@
import { createHash, randomBytes } from "node:crypto";
import { lstat, readFile, readdir } from "node:fs/promises";
import { join } from "node:path";
import { expect, type Page } from "@playwright/test";
import { pollUntil, type RunnerApi } from "./api.js";
import { collectRunEvents } from "./run-observations.js";
import { createTaskThroughUi } from "./user-actions.js";
import { observeRunProcesses, watchDeniedTarget } from "./copilot-local-fixtures.js";
import { cursorNativeCaseDesigns, cursorNativePlanArtifactGate, hasCursorDenialBoundary, hasCursorPlanDecision, hasDeliveredCursorNativeRequest, hasExactCursorNativeResponse, type CursorNativeMethod } from "./cursor-native-cases.js";
import type { LiveFixtureValues } from "./live-fixtures.js";
import type { MatrixExecution } from "./types.js";
type Row = Record<string, any>;
type Check = { id: string; passed: boolean; detail: string };
/** Independent bounded snapshot; symlinks are recorded without following them. */
export async function cursorNativeWorkspaceSnapshot(root: string): Promise<Record<string, string>> {
const files: Record<string, string> = {}; let bytes = 0;
async function scan(relative = "") {
for (const entry of (await readdir(join(root, relative))).sort()) {
const name = relative ? `${relative}/${entry}` : entry; const path = join(root, name); const stat = await lstat(path);
if (Object.keys(files).length >= 2048) throw new Error("Cursor workspace proof exceeds file bound");
if (stat.isSymbolicLink()) throw new Error("Cursor workspace proof cannot authorize a symlink");
if (stat.isDirectory()) { files[`${name}/`] = "directory"; await scan(name); }
else if (stat.isFile()) {
bytes += stat.size;
if (stat.size > 4 * 1024 * 1024 || bytes > 32 * 1024 * 1024) throw new Error("Cursor workspace proof exceeds byte bound");
files[name] = createHash("sha256").update(await readFile(path)).digest("hex");
} else throw new Error("Cursor workspace proof found a special file");
}
}
await scan(); return files;
}
export async function runCursorNativeFlow(input: {
page: Page; api: RunnerApi; fixtures: LiveFixtureValues; execution: MatrixExecution; nonce: string;
workspacePath: string; deadlineAt: number;
observe(issue: Row, runs: Row[]): void;
capture(id: string, label: string, file: string): Promise<void>;
evidence(name: string, data: unknown): Promise<void>;
registerCleanupAssertion?(assertion: () => Promise<Check[]>): void;
}) {
const { page, api, fixtures, execution, nonce } = input;
const design = cursorNativeCaseDesigns.find(value => value.id === execution.task.id);
if (!design || execution.environment.id !== "local" || execution.profile.qualificationCandidate !== "cursor") throw new Error("Cursor native fixtures require the explicit isolated local Cursor candidate");
if (design.id === "native-write-deny-reconnect" && !input.registerCleanupAssertion) throw new Error("Cursor denial requires authoritative post-cleanup verification");
const checks: Check[] = []; let issue: Row = {}; let runs: Row[] = [];
const check = (id: string, passed: boolean, detail: string) => { checks.push({ id, passed, detail }); expect(passed, detail).toBe(true); };
const events = (runId: string) => collectRunEvents<Row>((afterSeq, limit) => api.get(`/api/heartbeat-runs/${runId}/events?afterSeq=${afterSeq}&limit=${limit}`));
const absent = async (path: string) => { try { await lstat(path); return false; } catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return true; throw error; } };
const agent = await api.get<Row>(`/api/agents/${fixtures.agent.id}`);
const configured = await api.patch<Row>(`/api/agents/${fixtures.agent.id}`, { adapterConfig: { ...agent.adapterConfig, acpxSessionMode: design.cursorMode, acpxPermissionMode: design.permissionMode } });
check("explicit-mode-policy", configured.adapterConfig.acpxSessionMode === design.cursorMode && configured.adapterConfig.acpxPermissionMode === design.permissionMode, "Public agent configuration selected mode and permission policy separately before provider startup");
await input.evidence("cursor-native-contract.json", { caseId: design.id, mode: design.cursorMode, permissionMode: design.permissionMode, method: design.method, expectedRunCount: 1, nativeCallbackObserved: false, artifactGate: cursorNativePlanArtifactGate });
const project = await api.post<Row>(`/api/companies/${fixtures.company.id}/projects`, {
name: `Cursor native workspace ${nonce}`, executionWorkspacePolicy: { enabled: true, defaultMode: "shared_workspace", sharedWorkspaceConcurrency: "serialize", allowIssueOverride: false, environmentId: fixtures.environment.id, workspaceStrategy: { type: "project_primary" } },
workspace: { name: "Primary", sourceType: "local_path", cwd: input.workspacePath, isPrimary: true },
});
const baseline = await cursorNativeWorkspaceSnapshot(input.workspacePath);
const sampleWorkspace = async (phase: string) => {
const current = await cursorNativeWorkspaceSnapshot(input.workspacePath);
await input.evidence(`cursor-workspace-${phase}.json`, { baseline, current });
check(`workspace-unchanged-${phase}`, JSON.stringify(current) === JSON.stringify(baseline), "Independent workspace bytes remain unchanged by a pending/rejected/cancelled native plan or question");
};
const deniedPath = join(input.workspacePath, `cursor-denied-${nonce}.txt`);
const samples: Array<{ phase: string; path: string; absent: boolean; observedAt: number }> = [];
const sampleDenied = async (phase: string) => { const sample = { phase, path: deniedPath, absent: await absent(deniedPath), observedAt: Date.now() }; samples.push(sample); await input.evidence("cursor-denial-samples.json", samples); check(`denied-absent-${phase}`, sample.absent, "Independent denied target remains absent"); };
const processObserver = observeRunProcesses(); let processAuthority: string | null = null; let processObservationError = false;
const observeProcesses = () => {
const run = runs[0];
const authority = run?.processPid ? { pid: run.processPid, groupId: run.processGroupId, startedAt: run.processStartedAt, runId: run.id } : undefined;
if (authority) {
const key = JSON.stringify(authority);
if (processAuthority !== null && processAuthority !== key) processObservationError = true;
processAuthority ??= key;
}
return processObserver.sample(authority);
};
let processes = observeProcesses();
let deniedRequest: Row | null = null;
let processTimer: ReturnType<typeof setInterval> | undefined;
const watch = design.id === "native-write-deny-reconnect" ? watchDeniedTarget(input.workspacePath, `cursor-denied-${nonce}.txt`) : null;
if (watch) {
processTimer = setInterval(() => { try { processes = observeProcesses(); } catch { processObservationError = true; } }, 250);
input.registerCleanupAssertion!(async () => {
const cleanupChecks: Check[] = [];
const finalCheck = (id: string, passed: boolean, detail: string) => { cleanupChecks.push({ id, passed, detail }); };
try {
if (issue.id) await load();
processes = observeProcesses();
if (processes.captured && processes.live.length > 0 && !processObservationError) {
processes = await pollUntil({ label: "observed Cursor provider retirement", deadlineAt: Date.now() + 5_000,
load: async () => observeProcesses(), accept: observation => observation.live.length === 0 });
}
const settled = runs.length === 1 && ["succeeded", "failed", "cancelled", "timed_out"].includes(runs[0]!.status);
finalCheck("authoritative-provider-cleanup", settled && !processObservationError && processes.captured && processes.live.length === 0, "Exact API-bound per-turn process/start/group and observed descendants have retired");
const finalSample = { phase: "after-cleanup", path: deniedPath, absent: await absent(deniedPath), observedAt: Date.now() };
samples.push(finalSample);
finalCheck("denied-absent-after-cleanup", finalSample.absent, "Independent target remains absent after observed provider retirement");
const journal = watch.finish();
finalCheck("continuous-denial-observation", journal.complete && journal.targetMutationCount === 0, "Continuous target watcher observed no create/delete mutation and retained directory identity");
finalCheck("complete-native-denial-boundary", Boolean(deniedRequest) && hasCursorDenialBoundary({ request: deniedRequest, expectedRequestId: deniedRequest?.requestId ?? "", expectedToolCallId: deniedRequest?.details.toolCallId ?? "", path: deniedPath, samples }), "Supported native denial preserved the target through all six independent boundaries");
await input.evidence("cursor-native-denial-final.json", { request: deniedRequest, samples, processes, processObservationError, watcher: journal, checks: cleanupChecks });
if (cleanupChecks.some(row => !row.passed)) throw new Error("Cursor native denial cleanup proof is incomplete or observed an effect");
return cleanupChecks;
} finally {
clearInterval(processTimer);
await input.evidence("cursor-native-denial-cleanup-attempt.json", { request: deniedRequest, samples, processes, processObservationError, watcher: watch.finish(), checks: cleanupChecks });
}
});
}
const load = async () => {
issue = await api.get<Row>(`/api/issues/${issue.id}`);
const listed = await api.get<Row[]>(`/api/companies/${fixtures.company.id}/heartbeat-runs?limit=100`);
runs = await Promise.all(listed.map(run => api.get<Row>(`/api/heartbeat-runs/${run.id}`)));
runs.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt))); input.observe(issue, runs);
if (watch) processes = observeProcesses();
const interactions = await api.get<Row[]>(`/api/issues/${issue.id}/interactions`);
const runEvents = runs.length === 1 ? await events(runs[0]!.id) : [];
return { issue, runs, interactions, runEvents };
};
const reject = (state: Awaited<ReturnType<typeof load>>) => state.runs.length > 1 ? "Unexpected extra Cursor provider run" : state.runs.some(run => ["failed", "cancelled", "timed_out"].includes(run.status)) ? "Cursor provider run failed" : undefined;
const createdRequest = (rows: Row[], method: CursorNativeMethod, requestId?: string) => rows.map(row => row.payload?.prpEvent).find(event => event?.eventType === "runtime_request.created" && event.payload?.request?.origin?.adapter === "acpx-runtime" && event.payload.request.origin.provider === "cursor" && event.payload.request.origin.method === method && (!requestId || event.payload.request.requestId === requestId));
async function pending(seen: Set<string>) {
const state = await pollUntil({ label: "exact native Cursor callback", deadlineAt: input.deadlineAt, load,
reject: state => reject(state) ?? (state.runs.some(run => run.status === "succeeded") ? "Native Cursor callback was not observed before completion; qualification remains pending" : undefined),
accept: state => state.interactions.some(card => card.status === "pending" && !seen.has(card.id) && card.payload?.runtimeRequestId && createdRequest(state.runEvents, design!.method, card.payload.runtimeRequestId)) });
const cards = state.interactions.filter(card => card.status === "pending"); check("single-native-request", cards.length === 1 && state.runs.length === 1, "Exactly one native request belongs to one original provider run");
const card = cards[0]!; const event = createdRequest(state.runEvents, design!.method, card.payload.runtimeRequestId)!;
check("native-card-binding", card.sourceRunId === state.runs[0]!.id && card.continuationPolicy === "none" && JSON.stringify(card.payload.questionSet) === JSON.stringify(event.payload.request.input), "Durable card retains complete native input and exact source run");
await input.evidence(`cursor-native-${seen.size}-pending.json`, { card, event });
await page.reload(); const reloaded = (await load()).interactions.find(row => row.id === card.id);
check("browser-reconnect-identity", reloaded?.status === "pending" && reloaded?.payload.runtimeRequestId === card.payload.runtimeRequestId, "Browser reconnect preserves exact outstanding native request");
await input.capture(`cursor-native-${seen.size}`, "Native Cursor request pending", `cursor-native-${seen.size}.png`);
return { card, event };
}
async function delivery(card: Row, event: Row, response: Row) {
const identity = { runId: runs[0]!.id, turnId: event.turnId, requestId: card.payload.runtimeRequestId, method: design!.method, action: "submit" as const, response };
const state = await pollUntil({ label: "native response stream delivery", deadlineAt: input.deadlineAt, load, reject,
accept: state => state.interactions.some(row => row.id === card.id && row.status === "answered") && hasExactCursorNativeResponse({ ...identity, events: state.runEvents }) });
check("exact-native-delivery", hasExactCursorNativeResponse({ ...identity, events: state.runEvents }), "Exact displayed answer reached the native response writer and produced one ordered delivery receipt");
await input.evidence(`cursor-native-${card.id}-delivered.json`, { interaction: state.interactions.find(row => row.id === card.id), events: state.runEvents, response });
}
let expectedMarker = execution.task.buildVisibleMarker(nonce);
try {
if (design.id === "native-write-deny-reconnect") await sampleDenied("before-request");
await createTaskThroughUi({ page, issuePrefix: fixtures.company.issuePrefix!, agentName: fixtures.agent.name, title: execution.task.buildTitle(nonce), prompt: execution.task.buildPrompt(nonce), workMode: "standard", projectName: project.name });
issue = await pollUntil({ label: "browser-created Cursor task", deadlineAt: input.deadlineAt, load: async () => (await api.get<Row[]>(`/api/companies/${fixtures.company.id}/issues?limit=100`)).find(row => row.title === execution.task.buildTitle(nonce)), accept: Boolean }) ?? {};
if (!issue.id) throw new Error("Browser-created Cursor task is absent");
await page.goto(`/${fixtures.company.issuePrefix}/issues/${issue.identifier ?? issue.id}`);
const seen = new Set<string>();
if (design.id === "native-question-reconnect") {
const { card, event } = await pending(seen); const questions = card.payload.questionSet.questions;
check("native-question-shape", questions.length === 2 && questions[0].answerMode === "single_select" && questions[1].answerMode === "multi_select", "Native single/multiple choice shape is preserved");
const color = randomBytes(1)[0]! % 2 === 0 ? "Cobalt" : "Amber";
const trees = randomBytes(1)[0]! % 2 === 0 ? ["Cedar", "Maple"] : ["Maple"];
await page.getByRole("radio", { name: color, exact: true }).last().click();
await page.getByRole("button", { name: "Next", exact: true }).last().click();
for (const tree of trees) await page.getByRole("checkbox", { name: tree, exact: true }).last().click();
const answers = Object.fromEntries(questions.map((question: Row, index: number) => [question.id, { selectedOptionIds: (index === 0 ? [color] : trees).map(label => question.options.find((option: Row) => option.label === label)?.id) }]));
await page.getByRole("button", { name: card.payload.questionSet.submitLabel ?? "Submit answers", exact: true }).last().click();
await delivery(card, event, { schema: "paperclip.question_response.v1", answers });
expectedMarker = `CURSOR-NATIVE-${nonce}-${color.toLowerCase()}-${trees.map(tree => tree.toLowerCase()).sort().join("+")}`;
await sampleWorkspace("question-delivered");
} else if (design.method === "cursor/create_plan") {
const decisions = design.id === "native-plan-cancel" ? ["cancel"] as const : ["reject", "accept"] as const;
const feedbackMarker = `revision-${randomBytes(12).toString("hex")}`;
const feedback = `Include verification marker ${feedbackMarker} in the revised plan.`; let previousRevision: string | null = null;
for (const decision of decisions) {
const { card, event } = await pending(seen); const set = card.payload.questionSet; const plan = set.questions.find((question: Row) => /^plan-[a-f0-9]{64}$/.test(question.id));
check("full-plan-revision", typeof set.description === "string" && set.description.length > 0 && Boolean(plan) && plan.id !== previousRevision, "Complete native plan and a distinct content-bound revision are retained");
if (decision === "accept") check("revision-feedback", set.description.includes(feedbackMarker), "Revised native plan contains exact undisclosed rejection feedback");
for (const marker of [`CURSOR-PLAN-BEGIN-${nonce}`, `CURSOR-PLAN-END-${nonce}`]) {
check("complete-plan-boundary", set.description.includes(marker), "Retained native plan includes its full document boundaries");
await expect(page.getByRole("region", { name: "Question context" }).last()).toContainText(marker);
}
await sampleWorkspace(`plan-${decision}-pending`);
await page.getByRole("radio", { name: decision === "accept" ? "Accept plan" : decision === "reject" ? "Reject plan" : "Cancel plan request", exact: true }).last().click();
await page.getByRole("button", { name: "Next", exact: true }).last().click();
if (decision === "reject") await page.getByTestId("question-text-answer-composer").last().locator('[contenteditable="true"],textarea').first().fill(feedback);
const response = { schema: "paperclip.question_response.v1", answers: { [plan.id]: { selectedOptionIds: [decision] }, reason: decision === "reject" ? { text: feedback } : {} } };
check("revision-bound-decision", hasCursorPlanDecision(set, response, decision), "Decision addresses precisely the displayed native plan revision");
await page.getByRole("button", { name: set.submitLabel ?? "Submit answers", exact: true }).last().click();
await delivery(card, event, response); seen.add(card.id); previousRevision = plan.id;
if (decision !== "accept") await sampleWorkspace(`plan-${decision}-delivered`);
}
await input.evidence("cursor-native-artifact-gap.json", cursorNativePlanArtifactGate);
} else {
const state = await pollUntil({ label: "native Cursor permission", deadlineAt: input.deadlineAt, load, reject,
accept: state => Boolean(createdRequest(state.runEvents, "session/request_permission")) });
const event = createdRequest(state.runEvents, "session/request_permission")!; const request = event.payload.request; deniedRequest = request;
check("native-permission-identity", state.runs.length === 1 && typeof request.details?.toolCallId === "string" && request.choices.some((choice: Row) => choice.key === "decline"), "Presented native permission carries its tool identity and supported denial choice");
await sampleDenied("pending"); await page.reload();
const reloaded = await load(); check("permission-reconnect", Boolean(createdRequest(reloaded.runEvents, "session/request_permission", request.requestId)) && !reloaded.runEvents.some(row => row.payload?.prpEvent?.eventType === "runtime_request.resolved"), "Reconnect preserves the unresolved native permission");
await sampleDenied("browser-reconnected"); await input.capture("cursor-permission", "Native write permission awaiting denial", "cursor-permission.png");
const label = request.choices.find((choice: Row) => choice.key === "decline").label;
await page.getByRole("button", { name: label, exact: true }).last().click();
const identity = { runId: runs[0]!.id, turnId: event.turnId, requestId: request.requestId, method: "session/request_permission" as const, action: "decline" as const };
await pollUntil({ label: "native denial delivery", deadlineAt: input.deadlineAt, load, reject, accept: state => hasDeliveredCursorNativeRequest({ ...identity, events: state.runEvents }) });
await sampleDenied("after-decision");
}
const final = await pollUntil({ label: "Cursor native completion", deadlineAt: input.deadlineAt, load, reject,
accept: state => state.issue.status === "done" && state.runs.length === 1 && state.runs[0]!.status === "succeeded" && !state.interactions.some(card => card.status === "pending") });
check("one-native-run", final.runs[0]!.runtimeMode === "native", "Decision and completion remained in the original native provider run");
if (design.id === "native-write-deny-reconnect") await sampleDenied("after-terminal");
else if (design.id === "native-plan-cancel" || design.id === "native-question-reconnect") await sampleWorkspace("native-terminal");
await page.reload(); await expect(page.getByText(expectedMarker, { exact: true }).last()).toBeVisible();
await expect(page.getByTestId("issue-detail-header").getByRole("button", { name: "Change status (current: Done)", exact: true })).toBeVisible();
const comments = await api.get<Row[]>(`/api/issues/${issue.id}/comments`);
await input.evidence("api-state.json", { ...final, run: runs[0], comments, checks, runEventsByRun: [{ runId: runs[0]!.id, events: final.runEvents }] });
await input.capture("final-state", "Cursor native callback fixture verified", "final-state.png");
return { issue, runs, checks };
} finally { await input.evidence("cursor-native-checks.json", { issue, runs, checks }); }
}