import nodeFs from "node:fs"; import fs from "node:fs/promises"; import path from "node:path"; import type {
EmbeddedRunAttemptParams,
EmbeddedRunAttemptResult,
} from "openclaw/plugin-sdk/agent-harness"; import { resolveUserPath } from "openclaw/plugin-sdk/agent-harness";
async function assertNoSymlinkParents(filePath: string): Promise<void> { const resolvedDir = path.resolve(path.dirname(filePath)); const parsed = path.parse(resolvedDir); const relativeParts = path.relative(parsed.root, resolvedDir).split(path.sep).filter(Boolean);
let current = parsed.root; for (const part of relativeParts) {
current = path.join(current, part); const stat = await fs.lstat(current); if (stat.isSymbolicLink()) { if (path.dirname(current) === parsed.root) { continue;
} thrownew Error(`Refusing to write trajectory under symlinked directory: ${current}`);
} if (!stat.isDirectory()) { thrownew Error(`Refusing to write trajectory under non-directory: ${current}`);
}
}
}
function verifyStableOpenedTrajectoryFile(params: {
preOpenStat?: nodeFs.Stats;
postOpenStat: nodeFs.Stats;
filePath: string;
}): void { if (!params.postOpenStat.isFile()) { thrownew Error(`Refusing to write trajectory to non-file: ${params.filePath}`);
} if (params.postOpenStat.nlink > 1) { thrownew Error(`Refusing to write trajectory to hardlinked file: ${params.filePath}`);
} const pre = params.preOpenStat; if (pre && (pre.dev !== params.postOpenStat.dev || pre.ino !== params.postOpenStat.ino)) { thrownew Error(`Refusing to write trajectory after file changed: ${params.filePath}`);
}
}
async function safeAppendTrajectoryFile(filePath: string, line: string): Promise<void> {
await assertNoSymlinkParents(filePath);
let preOpenStat: nodeFs.Stats | undefined; try { const stat = await fs.lstat(filePath); if (stat.isSymbolicLink()) { thrownew Error(`Refusing to write trajectory through symlink: ${filePath}`);
} if (!stat.isFile()) { thrownew Error(`Refusing to write trajectory to non-file: ${filePath}`);
}
preOpenStat = stat;
} catch (err) { if ((err as NodeJS.ErrnoException).code !== "ENOENT") { throw err;
}
} const lineBytes = Buffer.byteLength(line, "utf8"); if ((preOpenStat?.size ?? 0) + lineBytes > TRAJECTORY_RUNTIME_FILE_MAX_BYTES) { return;
}
function parseTrajectoryEnabled(env: NodeJS.ProcessEnv): boolean { const value = env.OPENCLAW_TRAJECTORY?.trim().toLowerCase(); if (value === "1" || value === "true" || value === "yes" || value === "on") { returntrue;
} if (value === "0" || value === "false" || value === "no" || value === "off") { returnfalse;
} returntrue;
}
function sanitizeValue(value: unknown, depth = 0, key = ""): unknown { if (value == null || typeof value === "boolean" || typeof value === "number") { return value;
} if (typeof value === "string") { if (SENSITIVE_FIELD_RE.test(key)) { return"<redacted>";
} if (value.startsWith("data:") && value.length > 256) { return `<redacted data-uri ${value.slice(0, value.indexOf(",")).length} chars>`;
} if (PRIVATE_PAYLOAD_FIELD_RE.test(key) && value.length > 256) { return"<redacted payload>";
} const redacted = redactSensitiveString(value); return redacted.length > 20_000 ? `${redacted.slice(0, 20_000)}…` : redacted;
} if (depth >= 6) { return"<truncated>";
} if (Array.isArray(value)) { return value.slice(0, 100).map((entry) => sanitizeValue(entry, depth + 1, key));
} if (typeof value === "object") { const next: Record<string, unknown> = {}; for (const [key, child] of Object.entries(value).slice(0, 100)) {
next[key] = sanitizeValue(child, depth + 1, key);
} return next;
} return JSON.stringify(value);
}
function redactSensitiveString(value: string): string { return value
.replace(AUTHORIZATION_VALUE_RE, "$1 <redacted>")
.replace(JWT_VALUE_RE, "<redacted-jwt>")
.replace(COOKIE_PAIR_RE, "$1=<redacted>");
}
export function normalizeCodexTrajectoryError(value: unknown): string | null { if (!value) { returnnull;
} if (value instanceof Error) { return value.message;
} if (typeof value === "string") { return value;
} try { return JSON.stringify(value);
} catch { return"Unknown error";
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.24Bemerkung:
(vorverarbeitet am 2026-06-10)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.