mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 21:59:38 +01:00
Merge branch 'codex/runner-cursor-acp' into codex/runner-copilot-acp
* codex/runner-cursor-acp: Admit the verified Cursor v3 identity in Rust Admit current ACP profiles and verify cross-language pin agreement Prove Cursor SDK interactions and retain profile v3 build evidence
This commit is contained in:
commit
550801f956
8 files changed
+386
-8
No files matched your search
@@ -211,3 +211,32 @@ Prior Product/Runner results above measured the earlier unpatched runtime. The
|
||||
profile must retain pending qualification until the rebuilt final foundation
|
||||
runtime passes the remaining native interaction, durable approval, cancellation,
|
||||
recovery, accounting, and authenticated target-platform gates.
|
||||
|
||||
### Frozen profile v3 build
|
||||
|
||||
[The retained build record](../../packages/paperclip-runner/test/fixtures/cursor-acp/production-v3-offline-build.json)
|
||||
identifies exact runtime source `526081b9d6c905de5e2da680e3a37c620899b380`,
|
||||
profile v3, the ARM64 portable package and matching release daemon, both lock
|
||||
hashes, the committed ACPX patch hash, and credential-free launch results.
|
||||
Dependencies were installed into a fresh private store with copied files to
|
||||
avoid borrowing mutable package bytes from another worktree. The initial frozen
|
||||
install failed because the committed lock's patch configuration differed; that
|
||||
failure is retained. A private derived staging lock then passed frozen install.
|
||||
The repository lock was not changed.
|
||||
|
||||
The package's real immutable lease passed ACP initialization, required
|
||||
authentication for model/session discovery and creation, rejected unsupported
|
||||
fork/resume methods, and closed with no stderr. The TypeScript and release daemon
|
||||
builds passed. Twenty-six installed-package contracts passed. Another 184 control
|
||||
tests passed, including two Cursor-specific tests using the real installed ACP
|
||||
SDK stream: sessionless native single/multi-selection and complete revision-bound
|
||||
plan replies preserve request ID zero, await response pipe delivery, reject stale
|
||||
plan revisions, and suppress late answers after cancellation. The surrounding
|
||||
suite covers mocked admission, permission identity, provider-loss expiry,
|
||||
recovery fences, and cleanup ownership. These are deterministic tests, not live
|
||||
native interaction, durable browser approval, or provider-death qualification.
|
||||
|
||||
The new `cursor-runtime-patch.mjs` is present in the Docker build context. It is
|
||||
also a required Daytona content-identity input: changing patch source must change
|
||||
the image identity even when another declared input is unchanged. Publishing or
|
||||
qualification must use an image whose content hash includes that script.
|
||||
@@ -178,7 +178,7 @@ impl AcpxProviderDescriptor {
|
||||
"2026.09.26-dd393fe",
|
||||
None,
|
||||
None,
|
||||
"sha256:1157a5d071abbd57ab132f22bace75c65e84cc47a045b0023475488755e14899",
|
||||
"sha256:c91aa592ec867071ec4457b9b7230399ce42ad918787ceed99b4b651ea5607e2",
|
||||
),
|
||||
"copilot" => (
|
||||
self.model.as_str(),
|
||||
@@ -2722,7 +2722,7 @@ mod tests {
|
||||
"cursor",
|
||||
"cursor-agent",
|
||||
"2026.09.26-dd393fe",
|
||||
"sha256:1157a5d071abbd57ab132f22bace75c65e84cc47a045b0023475488755e14899",
|
||||
"sha256:c91aa592ec867071ec4457b9b7230399ce42ad918787ceed99b4b651ea5607e2",
|
||||
None,
|
||||
None,
|
||||
"explicit-model",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { QUALIFIED_ACPX_PROFILES } from "../drivers/acpx/qualified-profiles.js";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { buildNativeModelEnvelope, parseNativeExecutionInput, NATIVE_EXECUTION_INPUT_SCHEMA, type NativeExecutionInputV1 } from "./native-execution.js";
|
||||
@@ -280,7 +281,7 @@ describe("NativeExecutionInputV1", () => {
|
||||
})).toThrow("eventExpiryDays");
|
||||
});
|
||||
|
||||
it.each([1, 2, 3, 4, 5] as const)("accepts only a closed ACPX profile matching the driver and agent at profile version %s", (agentProfileVersion) => {
|
||||
it.each([1, 2, 3, 4, 5, 6] as const)("accepts only a closed ACPX profile matching the driver and agent at profile version %s", (agentProfileVersion) => {
|
||||
const provider = {
|
||||
kind: "acpx",
|
||||
agent: "pi",
|
||||
@@ -312,7 +313,7 @@ describe("NativeExecutionInputV1", () => {
|
||||
profile: provider.profile,
|
||||
});
|
||||
expect(parseNativeExecutionInput(parsed)).toEqual(parsed);
|
||||
for (const unsupportedVersion of [0, 6, 1.5, "5", null]) {
|
||||
for (const unsupportedVersion of [0, 7, 1.5, "6", null]) {
|
||||
expect(() => parseNativeExecutionInput({
|
||||
...input,
|
||||
session: { ...input.session, driverKind: "acpx_runtime" },
|
||||
@@ -332,6 +333,26 @@ describe("NativeExecutionInputV1", () => {
|
||||
})).toThrow("does not match");
|
||||
});
|
||||
|
||||
it.each(Object.values(QUALIFIED_ACPX_PROFILES))(
|
||||
"admits the current $agent profile through the native execution boundary",
|
||||
(declaration) => {
|
||||
const { qualificationModel, reportedModelId: _reported, permissionPolicy,
|
||||
modelPolicy: _modelPolicy, qualificationStatus: _status, ...profile } = declaration;
|
||||
const provider = {
|
||||
kind: "acpx", agent: declaration.agent,
|
||||
model: qualificationModel || "explicit-provider-model",
|
||||
permissionPolicy, profile,
|
||||
};
|
||||
const parsed = parseNativeExecutionInput({
|
||||
...input,
|
||||
session: { ...input.session, driverKind: "acpx_runtime" },
|
||||
provider,
|
||||
});
|
||||
expect(parsed.provider).toEqual(provider);
|
||||
expect(parseNativeExecutionInput(parsed)).toEqual(parsed);
|
||||
},
|
||||
);
|
||||
|
||||
it("defaults legacy lifecycle state to per-turn and validates warm timeouts", () => {
|
||||
const legacy = structuredClone(input) as Record<string, unknown>;
|
||||
delete (legacy.session as Record<string, unknown>).lifecyclePolicy;
|
||||
|
||||
@@ -90,7 +90,7 @@ export interface NativeAcpxProfileSnapshot {
|
||||
protocolVersion: 1;
|
||||
acpxVersion: "0.13.1";
|
||||
agent: NativeAcpxAgent;
|
||||
agentProfileVersion: 1 | 2 | 3 | 4 | 5;
|
||||
agentProfileVersion: 1 | 2 | 3 | 4 | 5 | 6;
|
||||
agentServerPackage: string;
|
||||
agentServerVersion: string;
|
||||
agentRuntimePackage: string | null;
|
||||
@@ -596,7 +596,7 @@ export function parseNativeExecutionInput(value: unknown): NativeExecutionInput
|
||||
|| profile.protocolVersion !== 1
|
||||
|| profile.acpxVersion !== "0.13.1"
|
||||
|| profile.agent !== provider.agent
|
||||
|| (profile.agentProfileVersion !== 1 && profile.agentProfileVersion !== 2 && profile.agentProfileVersion !== 3 && profile.agentProfileVersion !== 4 && profile.agentProfileVersion !== 5)
|
||||
|| (profile.agentProfileVersion !== 1 && profile.agentProfileVersion !== 2 && profile.agentProfileVersion !== 3 && profile.agentProfileVersion !== 4 && profile.agentProfileVersion !== 5 && profile.agentProfileVersion !== 6)
|
||||
) {
|
||||
throw new NativeExecutionInputError("input.provider.profile does not match the qualified ACPX v1 profile");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { createRequire } from "node:module";
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { expect, it } from "vitest";
|
||||
import { createCursorProfileExtensionAdapter } from "./cursor-extensions.js";
|
||||
const require = createRequire(import.meta.url);
|
||||
const acpxRoot = dirname(require.resolve("acpx/package.json"));
|
||||
const { k: AcpClient } = await import(pathToFileURL(join(acpxRoot, "dist/live-checkpoint-BSIrfgVo.js")).href);
|
||||
// The fixture uses native Cursor's sessionless extension shape and request ID 0.
|
||||
// The real installed ACP SDK handles the pipe; the production adapter normalizes
|
||||
// answers. This is credential-free protocol coverage, not native availability.
|
||||
const fixture = String.raw`
|
||||
const send=value=>process.stdout.write(JSON.stringify({jsonrpc:'2.0',...value})+'\n');let prompt;
|
||||
require('node:readline').createInterface({input:process.stdin}).on('line',line=>{
|
||||
const m=JSON.parse(line);
|
||||
if(m.method==='initialize')send({id:m.id,result:{protocolVersion:1,agentCapabilities:{},authMethods:[]}});
|
||||
else if(m.method==='session/new')send({id:m.id,result:{sessionId:'cursor-session'}});
|
||||
else if(m.method==='session/prompt'){
|
||||
prompt=m.id;const plan=m.params.prompt[0].text==='plan';
|
||||
send({id:0,method:plan?'cursor/create_plan':'cursor/ask_question',params:plan
|
||||
?{toolCallId:'plan',name:'Complete revision',plan:'1. Read\n2. Verify',todos:[],phases:[]}
|
||||
:{toolCallId:'question',questions:[{id:'native-single',prompt:'Color',options:[{id:'c',label:'Cobalt'},{id:'a',label:'Amber'}]},{id:'native-multi',prompt:'Trees',allowMultiple:true,options:[{id:'cedar',label:'Cedar'},{id:'maple',label:'Maple'}]}]}});
|
||||
}else if(m.method==='session/cancel'){send({id:prompt,result:{stopReason:'cancelled'}});prompt=undefined;}
|
||||
else if(m.id===0&&!m.method&&prompt!==undefined){
|
||||
send({method:'session/update',params:{sessionId:'cursor-session',update:{sessionUpdate:'agent_message_chunk',content:{type:'text',text:JSON.stringify(m)}}}});
|
||||
send({id:prompt,result:{stopReason:'end_turn'}});prompt=undefined;
|
||||
}
|
||||
});`;
|
||||
type Context = { requestId: string | number; signal: AbortSignal; responseDelivery?: Promise<void> };
|
||||
async function withClient(onRequest: (method: string, params: Record<string, unknown>, context: Context) => Promise<unknown>, run: (client: InstanceType<typeof AcpClient>, received: unknown[]) => Promise<void>) {
|
||||
const cwd = await mkdtemp(join(tmpdir(), "cursor-sdk-")); const received: unknown[] = [];
|
||||
const client = new AcpClient({ agentCommand: "owned-fixture", cwd, permissionMode: "approve-all", fs: false, terminal: false, authPolicy: "skip", suppressSdkConsoleErrors: true,
|
||||
extensionMethods: ["cursor/ask_question", "cursor/create_plan"], onExtensionRequest: onRequest,
|
||||
spawnAgent: () => spawn(process.execPath, ["-e", fixture], { cwd, env: { PATH: "/usr/bin:/bin" }, stdio: ["pipe", "pipe", "pipe"] }),
|
||||
onSessionUpdate: (params: { update: { content?: { type: string; text: string } } }) => { if (params.update.content?.type === "text") received.push(JSON.parse(params.update.content.text)); },
|
||||
});
|
||||
try { await client.start(); await client.createSession(); await run(client, received); }
|
||||
finally { await client.close(); await rm(cwd, { recursive: true, force: true }); }
|
||||
}
|
||||
it("roundtrips Cursor multi-select and revision-bound plan decisions through the installed ACP SDK", async () => {
|
||||
const deliveries: Promise<void>[] = []; let turn = 0;
|
||||
await withClient(async (method, params, context) => {
|
||||
expect(context.requestId).toBe(0); expect(params).not.toHaveProperty("sessionId");
|
||||
expect(context.responseDelivery).toBeInstanceOf(Promise); deliveries.push(context.responseDelivery!);
|
||||
const adapter = createCursorProfileExtensionAdapter({ workspacePath: tmpdir(), sessionId: "cursor-session", turnId: `turn-${++turn}` });
|
||||
const request = await adapter.request(method, { ...params, sessionId: "cursor-session" });
|
||||
if (!("input" in request)) throw new Error("Expected blocking Cursor interaction");
|
||||
const questionId = request.input.questionSet.questions[0]!.id;
|
||||
const answers = method === "cursor/ask_question"
|
||||
? { "question-1": { selectedOptionIds: ["option-1"] }, "question-2": { selectedOptionIds: ["option-1", "option-2"] } }
|
||||
: { [questionId]: { selectedOptionIds: ["accept"] } };
|
||||
if (method === "cursor/create_plan") {
|
||||
expect(request.input.questionSet.description).toBe("1. Read\n2. Verify");
|
||||
expect(() => request.input.resolve({ action: "submit", response: { schema: "paperclip.question_response.v1", answers: { "plan-stale": { selectedOptionIds: ["accept"] } } } })).toThrow();
|
||||
}
|
||||
return request.input.resolve({ action: "submit", response: { schema: "paperclip.question_response.v1", answers } });
|
||||
}, async (client, received) => {
|
||||
expect((await client.prompt("cursor-session", "question")).stopReason).toBe("end_turn");
|
||||
expect((await client.prompt("cursor-session", "plan")).stopReason).toBe("end_turn"); await Promise.all(deliveries);
|
||||
expect(received).toEqual([
|
||||
{ jsonrpc: "2.0", id: 0, result: { outcome: { outcome: "answered", answers: [{ questionId: "native-single", selectedOptionIds: ["c"] }, { questionId: "native-multi", selectedOptionIds: ["cedar", "maple"] }] } } },
|
||||
{ jsonrpc: "2.0", id: 0, result: { outcome: { outcome: "accepted" } } },
|
||||
]);
|
||||
});
|
||||
});
|
||||
it("cancels an unanswered Cursor request without delivering a late answer", async () => {
|
||||
const started = Promise.withResolvers<void>(); const response = Promise.withResolvers<unknown>(); let signal: AbortSignal | undefined;
|
||||
await withClient(async (_method, _params, context) => { signal = context.signal; started.resolve(); return response.promise; }, async (client, received) => {
|
||||
const prompt = client.prompt("cursor-session", "question"); await started.promise; await client.cancel("cursor-session"); await prompt;
|
||||
expect(signal?.aborted).toBe(true); response.resolve({ outcome: { outcome: "answered", answers: [] } });
|
||||
await new Promise(resolve => setImmediate(resolve)); expect(received).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
import {
|
||||
QUALIFIED_ACPX_PROFILES,
|
||||
@@ -6,6 +7,21 @@ import {
|
||||
} from "./qualified-profiles.js";
|
||||
|
||||
describe("qualified ACPX profiles", () => {
|
||||
it.each(Object.values(QUALIFIED_ACPX_PROFILES))(
|
||||
"keeps the Rust admission digest synchronized with the current $agent profile",
|
||||
(profile) => {
|
||||
const rust = readFileSync(new URL("../../../runner/crates/runner-core/src/acpx_provider_backend.rs", import.meta.url), "utf8");
|
||||
const start = rust.indexOf(`"${profile.agent}" => (`);
|
||||
expect(start).toBeGreaterThan(0);
|
||||
const end = rust.indexOf("\n ),", start);
|
||||
expect(end).toBeGreaterThan(start);
|
||||
const admission = rust.slice(start, end);
|
||||
expect(admission).toContain(JSON.stringify(profile.agentServerPackage));
|
||||
expect(admission).toContain(JSON.stringify(profile.agentServerVersion));
|
||||
expect(admission).toContain(JSON.stringify(profile.commandDigest));
|
||||
},
|
||||
);
|
||||
|
||||
it("binds each agent to one immutable package and model declaration", () => {
|
||||
for (const agent of ["pi", "claude", "codex", "grok"] as const) {
|
||||
const profile = QUALIFIED_ACPX_PROFILES[agent];
|
||||
|
||||
@@ -2,7 +2,7 @@ export const QUALIFIED_ACPX_VERSION = "0.13.1" as const;
|
||||
export const ACPX_DRIVER_KIND = "acpx_runtime" as const;
|
||||
export const ACPX_DRIVER_PROTOCOL_VERSION = 1 as const;
|
||||
|
||||
import type { NativeAcpxAgent } from "../../contracts/native-execution.js";
|
||||
import type { NativeAcpxAgent, NativeAcpxProfileSnapshot } from "../../contracts/native-execution.js";
|
||||
|
||||
export type QualifiedAcpxAgent = NativeAcpxAgent;
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface QualifiedAcpxProfile {
|
||||
readonly protocolVersion: typeof ACPX_DRIVER_PROTOCOL_VERSION;
|
||||
readonly acpxVersion: typeof QUALIFIED_ACPX_VERSION;
|
||||
readonly agent: QualifiedAcpxAgent;
|
||||
readonly agentProfileVersion: 1 | 2 | 3 | 4 | 5;
|
||||
readonly agentProfileVersion: NativeAcpxProfileSnapshot["agentProfileVersion"];
|
||||
readonly qualificationStatus?: "pending";
|
||||
readonly modelPolicy?: "explicit-provider-verified";
|
||||
/** Wire identity: an npm package name or a runner-owned builtin: identifier. */
|
||||
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
{
|
||||
"schema": "paperclip.cursor.production-offline-build.v1",
|
||||
"observedAt": "2026-09-29",
|
||||
"runtimeSourceRevision": "526081b9d6c905de5e2da680e3a37c620899b380",
|
||||
"providerVersion": "2026.09.26-dd393fe",
|
||||
"profileVersion": 3,
|
||||
"patchVersion": "paperclip-cursor-isolation-v1",
|
||||
"providerPackDigest": "sha256:7166a42d06192c53191aad7b15396e291675624001fbd95dd4b3cbda197f87a0",
|
||||
"candidate": {
|
||||
"version": "2026.09.26-dd393fe",
|
||||
"profileDigest": "sha256:c91aa592ec867071ec4457b9b7230399ce42ad918787ceed99b4b651ea5607e2",
|
||||
"closureDigest": "sha256:8a22016ca832ef8c8e6a83b955208d74c63f2c8dbe1dd688cbf27b81ad01886a",
|
||||
"qualification": "pending",
|
||||
"path": "provider-assets/cursor/darwin-arm64",
|
||||
"sha256": "sha256:e4c656debc32e8a6bf6e6fd7f620223b48ad0b8b18994096d09f056fdf9d809c"
|
||||
},
|
||||
"daemon": {
|
||||
"sourceRevision": "526081b9d6c905de5e2da680e3a37c620899b380",
|
||||
"platform": "darwin-arm64",
|
||||
"sha256": "826611379af7b8148933c58c6c77826c5a4f0773b789aac9eafbbc66b8e3dcb5"
|
||||
},
|
||||
"dependencyProvenance": {
|
||||
"source": "git archive of exact runtimeSourceRevision",
|
||||
"isolation": "fresh private pnpm store; copied package files, no shared hardlinks",
|
||||
"pnpmVersion": "9.15.4",
|
||||
"committedLockSha256": "e0c928a494f90ddad3c00791e83f09315ee8c82df2a0418a809dcf93649a8ab3",
|
||||
"stagingLockSha256": "9eea60187c6c808efb4d936a234a8d8000beac45253091abd4c5348132bd1408",
|
||||
"acpxPatchSha256": "8b698d853fe6df6a34a8f89dddaccc6c8f1e5b7763af5ab5866b77acd42bc127",
|
||||
"firstFrozenInstall": "failed: ERR_PNPM_LOCKFILE_CONFIG_MISMATCH",
|
||||
"resolution": "retained original lock, generated private staging lock only, then frozen installation passed; repository lock unchanged"
|
||||
},
|
||||
"checks": {
|
||||
"runnerTypescriptBuild": "passed",
|
||||
"runnerReleaseDaemonBuild": "passed",
|
||||
"freshPackageContracts": {
|
||||
"passed": 26
|
||||
},
|
||||
"runtimeControlsAndCursorSdkInteractions": {
|
||||
"passed": 184,
|
||||
"scope": "credential-free real ACP SDK fixtures plus mocked admission, recovery, request delivery, lifecycle and cancellation; excludes native provider availability"
|
||||
},
|
||||
"verifiedNativeLease": {
|
||||
"schema": "paperclip-cursor-offline-pack-proof/v1",
|
||||
"capturedAt": "2026-09-29T14:50:08.614Z",
|
||||
"runnerSourceRevision": "526081b9d6c905de5e2da680e3a37c620899b380",
|
||||
"packDigest": "sha256:7166a42d06192c53191aad7b15396e291675624001fbd95dd4b3cbda197f87a0",
|
||||
"target": {
|
||||
"platform": "darwin",
|
||||
"architecture": "arm64"
|
||||
},
|
||||
"candidate": {
|
||||
"version": "2026.09.26-dd393fe",
|
||||
"profileDigest": "sha256:c91aa592ec867071ec4457b9b7230399ce42ad918787ceed99b4b651ea5607e2",
|
||||
"closureDigest": "sha256:8a22016ca832ef8c8e6a83b955208d74c63f2c8dbe1dd688cbf27b81ad01886a",
|
||||
"qualification": "pending",
|
||||
"path": "provider-assets/cursor/darwin-arm64",
|
||||
"sha256": "sha256:e4c656debc32e8a6bf6e6fd7f620223b48ad0b8b18994096d09f056fdf9d809c"
|
||||
},
|
||||
"productionLock": "sha256:9eea60187c6c808efb4d936a234a8d8000beac45253091abd4c5348132bd1408",
|
||||
"launch": {
|
||||
"entrypoint": "verified bundled node + index.js",
|
||||
"arguments": [
|
||||
"--disable-project-configs",
|
||||
"--disable-auto-update",
|
||||
"acp"
|
||||
],
|
||||
"credentials": "none; private home/config/data/cache; memory credential store",
|
||||
"verification": "generic profile installation registry, whole closure digest verification, retained immutable native snapshot, closed module bootstrap",
|
||||
"modelAdmission": "offline-installation-probe is a factory identity sentinel only; no ACP model selection or prompt; model remains unverified"
|
||||
},
|
||||
"requests": [
|
||||
{
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"protocolVersion": 1,
|
||||
"clientCapabilities": {
|
||||
"fs": {
|
||||
"readTextFile": false,
|
||||
"writeTextFile": false
|
||||
},
|
||||
"terminal": false,
|
||||
"_meta": {
|
||||
"subagents": true
|
||||
}
|
||||
},
|
||||
"clientInfo": {
|
||||
"name": "paperclip-offline-pack-probe",
|
||||
"version": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "cursor/list_available_models",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"method": "session/list",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"method": "session/fork",
|
||||
"params": {
|
||||
"sessionId": "probe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session/resume",
|
||||
"params": {
|
||||
"sessionId": "probe",
|
||||
"cwd": "/qualification/private/workspace",
|
||||
"mcpServers": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"method": "session/new",
|
||||
"params": {
|
||||
"cwd": "/qualification/private/workspace",
|
||||
"mcpServers": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": [
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"result": {
|
||||
"protocolVersion": 1,
|
||||
"agentCapabilities": {
|
||||
"loadSession": true,
|
||||
"mcpCapabilities": {
|
||||
"http": true,
|
||||
"sse": true
|
||||
},
|
||||
"promptCapabilities": {
|
||||
"audio": false,
|
||||
"embeddedContext": false,
|
||||
"image": true
|
||||
},
|
||||
"sessionCapabilities": {
|
||||
"list": {},
|
||||
"subagents": {}
|
||||
}
|
||||
},
|
||||
"authMethods": [
|
||||
{
|
||||
"id": "cursor_login",
|
||||
"name": "Cursor Login",
|
||||
"description": "Authenticate using existing Cursor login credentials. Run 'agent login' first if not logged in."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 2,
|
||||
"error": {
|
||||
"code": -32000,
|
||||
"message": "Authentication required",
|
||||
"data": {
|
||||
"message": "Authentication required. Please run 'agent login' first, then call authenticate() with methodId 'cursor_login'."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 3,
|
||||
"error": {
|
||||
"code": -32000,
|
||||
"message": "Authentication required",
|
||||
"data": {
|
||||
"message": "Authentication required. Please run 'agent login' first, then call authenticate() with methodId 'cursor_login'."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 4,
|
||||
"error": {
|
||||
"code": -32601,
|
||||
"message": "\"Method not found\": session/fork",
|
||||
"data": {
|
||||
"method": "session/fork"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 5,
|
||||
"error": {
|
||||
"code": -32601,
|
||||
"message": "\"Method not found\": session/resume",
|
||||
"data": {
|
||||
"method": "session/resume"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 6,
|
||||
"error": {
|
||||
"code": -32000,
|
||||
"message": "Authentication required",
|
||||
"data": {
|
||||
"message": "Authentication required. Please run 'agent login' first, then call authenticate() with methodId 'cursor_login'."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"process": {
|
||||
"exitCode": 143,
|
||||
"signalCode": null,
|
||||
"leaseClosed": true,
|
||||
"stderrBytes": 0,
|
||||
"unexpectedMessageCount": 0
|
||||
},
|
||||
"qualification": {
|
||||
"status": "pending",
|
||||
"model": null,
|
||||
"billablePrompts": 0,
|
||||
"costUsd": 0,
|
||||
"environment": "local macOS ARM64; no Daytona execution"
|
||||
},
|
||||
"redactions": [
|
||||
"ephemeral workspace and private directories replaced with /qualification/private"
|
||||
]
|
||||
}
|
||||
},
|
||||
"qualification": {
|
||||
"status": "pending",
|
||||
"paidPrompts": 0,
|
||||
"authenticatedNativeInteractions": "not attempted",
|
||||
"durableRestrictiveApprovals": "not qualified",
|
||||
"authenticatedMacosX64": "not qualified",
|
||||
"authenticatedDaytona": "not qualified"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user