Files
Agent-System/deploy/pi-agent/result-v1.mjs
T
weichenandCursor 0a61cfbe90 feat(agent): add typed-result-v1 and fail-closed remote parser
Remote /v1/run now emits additive result_v1; Go validates run_id, digest, and paths before materializing, and only accepts missing typed payloads with an explicit legacy handshake.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 15:25:18 +00:00

83 lines
2.5 KiB
JavaScript

import { createHash } from "node:crypto";
export const RESULT_V1_SCHEMA_VERSION = 1;
export const MAX_RESULT_MEMBERS = 32;
export const MAX_RESULT_MEMBER_BYTES = 2 * 1024 * 1024;
const RETRYABLE_CODES = new Set(["TIMEOUT", "CANCELLED", "TRANSIENT"]);
const ALLOWED_CODES = new Set([
"PROMPT_EMPTY",
"UNAUTHORIZED",
"TIMEOUT",
"CANCELLED",
"TRANSIENT",
"MISSING_TARGET",
"RUN_FAILED",
]);
export function typedErrorCode(payload) {
const raw = String(payload?.fallback_reason || payload?.error || "");
const t = raw.toLowerCase();
if (t.includes("prompt_empty")) return "PROMPT_EMPTY";
if (t.includes("unauthorized")) return "UNAUTHORIZED";
if (t.includes("timeout") || t.includes("timed out")) return "TIMEOUT";
if (t.includes("cancel")) return "CANCELLED";
if (
t.includes("connection") ||
t.includes("terminated") ||
t.includes("econnreset") ||
t.includes("socket hang")
) {
return "TRANSIENT";
}
if (t.includes("missing target")) return "MISSING_TARGET";
if (payload?.retryable) return "TRANSIENT";
if (!payload?.ok) return "RUN_FAILED";
return "";
}
export function membersDigest(members) {
const rows = (Array.isArray(members) ? members : []).map((m) => ({
kind: m.kind,
path: m.path,
digest: m.digest,
size: m.size,
}));
return createHash("sha256").update(JSON.stringify(rows)).digest("hex");
}
export function withResultV1(payload) {
const out = { ...payload };
delete out.stderr;
const patches = Array.isArray(out.patches) ? out.patches : [];
const members = [];
for (const p of patches) {
const rel = String(p?.rel_path || "");
if (!rel || rel.includes("..") || rel.startsWith("/")) continue;
const buf = Buffer.from(String(p.content || ""), "utf8");
if (buf.length > MAX_RESULT_MEMBER_BYTES) continue;
if (members.length >= MAX_RESULT_MEMBERS) break;
members.push({
kind: "file",
path: rel,
digest: createHash("sha256").update(buf).digest("hex"),
size: buf.length,
});
}
const ok = !!out.ok;
const errorCode = ok ? "" : typedErrorCode(out);
const retryable = !ok && RETRYABLE_CODES.has(errorCode) && !!out.retryable;
out.capabilities = { typed_result: true };
out.runner_protocol = "typed-v1";
out.result_v1 = {
schema_version: RESULT_V1_SCHEMA_VERSION,
run_id: String(out.run_id || "unknown").slice(0, 128),
outcome: ok ? "success" : "error",
retryable,
error_code: ALLOWED_CODES.has(errorCode) ? errorCode : ok ? "" : "RUN_FAILED",
members,
content_digest: membersDigest(members),
};
return out;
}