chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,465 @@
|
||||
function severityRank(severity) {
|
||||
switch (severity) {
|
||||
case "critical":
|
||||
return 0;
|
||||
case "high":
|
||||
return 1;
|
||||
case "medium":
|
||||
return 2;
|
||||
default:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
function formatLineRange(finding) {
|
||||
if (!finding.line_start) {
|
||||
return "";
|
||||
}
|
||||
if (!finding.line_end || finding.line_end === finding.line_start) {
|
||||
return `:${finding.line_start}`;
|
||||
}
|
||||
return `:${finding.line_start}-${finding.line_end}`;
|
||||
}
|
||||
|
||||
function validateReviewResultShape(data) {
|
||||
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
||||
return "Expected a top-level JSON object.";
|
||||
}
|
||||
if (typeof data.verdict !== "string" || !data.verdict.trim()) {
|
||||
return "Missing string `verdict`.";
|
||||
}
|
||||
if (typeof data.summary !== "string" || !data.summary.trim()) {
|
||||
return "Missing string `summary`.";
|
||||
}
|
||||
if (!Array.isArray(data.findings)) {
|
||||
return "Missing array `findings`.";
|
||||
}
|
||||
if (!Array.isArray(data.next_steps)) {
|
||||
return "Missing array `next_steps`.";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeReviewFinding(finding, index) {
|
||||
const source = finding && typeof finding === "object" && !Array.isArray(finding) ? finding : {};
|
||||
const lineStart = Number.isInteger(source.line_start) && source.line_start > 0 ? source.line_start : null;
|
||||
const lineEnd =
|
||||
Number.isInteger(source.line_end) && source.line_end > 0 && (!lineStart || source.line_end >= lineStart)
|
||||
? source.line_end
|
||||
: lineStart;
|
||||
|
||||
return {
|
||||
severity: typeof source.severity === "string" && source.severity.trim() ? source.severity.trim() : "low",
|
||||
title: typeof source.title === "string" && source.title.trim() ? source.title.trim() : `Finding ${index + 1}`,
|
||||
body: typeof source.body === "string" && source.body.trim() ? source.body.trim() : "No details provided.",
|
||||
file: typeof source.file === "string" && source.file.trim() ? source.file.trim() : "unknown",
|
||||
line_start: lineStart,
|
||||
line_end: lineEnd,
|
||||
recommendation: typeof source.recommendation === "string" ? source.recommendation.trim() : ""
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeReviewResultData(data) {
|
||||
return {
|
||||
verdict: data.verdict.trim(),
|
||||
summary: data.summary.trim(),
|
||||
findings: data.findings.map((finding, index) => normalizeReviewFinding(finding, index)),
|
||||
next_steps: data.next_steps
|
||||
.filter((step) => typeof step === "string" && step.trim())
|
||||
.map((step) => step.trim())
|
||||
};
|
||||
}
|
||||
|
||||
function isStructuredReviewStoredResult(storedJob) {
|
||||
const result = storedJob?.result;
|
||||
if (!result || typeof result !== "object" || Array.isArray(result)) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
Object.prototype.hasOwnProperty.call(result, "result") ||
|
||||
Object.prototype.hasOwnProperty.call(result, "parseError")
|
||||
);
|
||||
}
|
||||
|
||||
function formatJobLine(job) {
|
||||
const parts = [job.id, `${job.status || "unknown"}`];
|
||||
if (job.kindLabel) {
|
||||
parts.push(job.kindLabel);
|
||||
}
|
||||
if (job.title) {
|
||||
parts.push(job.title);
|
||||
}
|
||||
return parts.join(" | ");
|
||||
}
|
||||
|
||||
function escapeMarkdownCell(value) {
|
||||
return String(value ?? "")
|
||||
.replace(/\|/g, "\\|")
|
||||
.replace(/\r?\n/g, " ")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function formatCodexResumeCommand(job) {
|
||||
if (!job?.threadId) {
|
||||
return null;
|
||||
}
|
||||
return `codex resume ${job.threadId}`;
|
||||
}
|
||||
|
||||
function appendActiveJobsTable(lines, jobs) {
|
||||
lines.push("Active jobs:");
|
||||
lines.push("| Job | Kind | Status | Phase | Elapsed | Codex Session ID | Summary | Actions |");
|
||||
lines.push("| --- | --- | --- | --- | --- | --- | --- | --- |");
|
||||
for (const job of jobs) {
|
||||
const actions = [`/codex:status ${job.id}`];
|
||||
if (job.status === "queued" || job.status === "running") {
|
||||
actions.push(`/codex:cancel ${job.id}`);
|
||||
}
|
||||
lines.push(
|
||||
`| ${escapeMarkdownCell(job.id)} | ${escapeMarkdownCell(job.kindLabel)} | ${escapeMarkdownCell(job.status)} | ${escapeMarkdownCell(job.phase ?? "")} | ${escapeMarkdownCell(job.elapsed ?? "")} | ${escapeMarkdownCell(job.threadId ?? "")} | ${escapeMarkdownCell(job.summary ?? "")} | ${actions.map((action) => `\`${action}\``).join("<br>")} |`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function pushJobDetails(lines, job, options = {}) {
|
||||
lines.push(`- ${formatJobLine(job)}`);
|
||||
if (job.summary) {
|
||||
lines.push(` Summary: ${job.summary}`);
|
||||
}
|
||||
if (job.phase) {
|
||||
lines.push(` Phase: ${job.phase}`);
|
||||
}
|
||||
if (options.showElapsed && job.elapsed) {
|
||||
lines.push(` Elapsed: ${job.elapsed}`);
|
||||
}
|
||||
if (options.showDuration && job.duration) {
|
||||
lines.push(` Duration: ${job.duration}`);
|
||||
}
|
||||
if (job.threadId) {
|
||||
lines.push(` Codex session ID: ${job.threadId}`);
|
||||
}
|
||||
const resumeCommand = formatCodexResumeCommand(job);
|
||||
if (resumeCommand) {
|
||||
lines.push(` Resume in Codex: ${resumeCommand}`);
|
||||
}
|
||||
if (job.logFile && options.showLog) {
|
||||
lines.push(` Log: ${job.logFile}`);
|
||||
}
|
||||
if ((job.status === "queued" || job.status === "running") && options.showCancelHint) {
|
||||
lines.push(` Cancel: /codex:cancel ${job.id}`);
|
||||
}
|
||||
if (job.status !== "queued" && job.status !== "running" && options.showResultHint) {
|
||||
lines.push(` Result: /codex:result ${job.id}`);
|
||||
}
|
||||
if (job.status !== "queued" && job.status !== "running" && job.jobClass === "task" && job.write && options.showReviewHint) {
|
||||
lines.push(" Review changes: /codex:review --wait");
|
||||
lines.push(" Stricter review: /codex:adversarial-review --wait");
|
||||
}
|
||||
if (job.progressPreview?.length) {
|
||||
lines.push(" Progress:");
|
||||
for (const line of job.progressPreview) {
|
||||
lines.push(` ${line}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function appendReasoningSection(lines, reasoningSummary) {
|
||||
if (!Array.isArray(reasoningSummary) || reasoningSummary.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
lines.push("", "Reasoning:");
|
||||
for (const section of reasoningSummary) {
|
||||
lines.push(`- ${section}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderSetupReport(report) {
|
||||
const lines = [
|
||||
"# Codex Setup",
|
||||
"",
|
||||
`Status: ${report.ready ? "ready" : "needs attention"}`,
|
||||
"",
|
||||
"Checks:",
|
||||
`- node: ${report.node.detail}`,
|
||||
`- npm: ${report.npm.detail}`,
|
||||
`- codex: ${report.codex.detail}`,
|
||||
`- auth: ${report.auth.detail}`,
|
||||
`- session runtime: ${report.sessionRuntime.label}`,
|
||||
`- review gate: ${report.reviewGateEnabled ? "enabled" : "disabled"}`,
|
||||
""
|
||||
];
|
||||
|
||||
if (report.actionsTaken.length > 0) {
|
||||
lines.push("Actions taken:");
|
||||
for (const action of report.actionsTaken) {
|
||||
lines.push(`- ${action}`);
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
if (report.nextSteps.length > 0) {
|
||||
lines.push("Next steps:");
|
||||
for (const step of report.nextSteps) {
|
||||
lines.push(`- ${step}`);
|
||||
}
|
||||
}
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
|
||||
export function renderReviewResult(parsedResult, meta) {
|
||||
if (!parsedResult.parsed) {
|
||||
const lines = [
|
||||
`# Codex ${meta.reviewLabel}`,
|
||||
"",
|
||||
"Codex did not return valid structured JSON.",
|
||||
"",
|
||||
`- Parse error: ${parsedResult.parseError}`
|
||||
];
|
||||
|
||||
if (parsedResult.rawOutput) {
|
||||
lines.push("", "Raw final message:", "", "```text", parsedResult.rawOutput, "```");
|
||||
}
|
||||
|
||||
appendReasoningSection(lines, meta.reasoningSummary ?? parsedResult.reasoningSummary);
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
|
||||
const validationError = validateReviewResultShape(parsedResult.parsed);
|
||||
if (validationError) {
|
||||
const lines = [
|
||||
`# Codex ${meta.reviewLabel}`,
|
||||
"",
|
||||
`Target: ${meta.targetLabel}`,
|
||||
"Codex returned JSON with an unexpected review shape.",
|
||||
"",
|
||||
`- Validation error: ${validationError}`
|
||||
];
|
||||
|
||||
if (parsedResult.rawOutput) {
|
||||
lines.push("", "Raw final message:", "", "```text", parsedResult.rawOutput, "```");
|
||||
}
|
||||
|
||||
appendReasoningSection(lines, meta.reasoningSummary ?? parsedResult.reasoningSummary);
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
|
||||
const data = normalizeReviewResultData(parsedResult.parsed);
|
||||
const findings = [...data.findings].sort((left, right) => severityRank(left.severity) - severityRank(right.severity));
|
||||
const lines = [
|
||||
`# Codex ${meta.reviewLabel}`,
|
||||
"",
|
||||
`Target: ${meta.targetLabel}`,
|
||||
`Verdict: ${data.verdict}`,
|
||||
"",
|
||||
data.summary,
|
||||
""
|
||||
];
|
||||
|
||||
if (findings.length === 0) {
|
||||
lines.push("No material findings.");
|
||||
} else {
|
||||
lines.push("Findings:");
|
||||
for (const finding of findings) {
|
||||
const lineSuffix = formatLineRange(finding);
|
||||
lines.push(`- [${finding.severity}] ${finding.title} (${finding.file}${lineSuffix})`);
|
||||
lines.push(` ${finding.body}`);
|
||||
if (finding.recommendation) {
|
||||
lines.push(` Recommendation: ${finding.recommendation}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.next_steps.length > 0) {
|
||||
lines.push("", "Next steps:");
|
||||
for (const step of data.next_steps) {
|
||||
lines.push(`- ${step}`);
|
||||
}
|
||||
}
|
||||
|
||||
appendReasoningSection(lines, meta.reasoningSummary);
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
|
||||
export function renderNativeReviewResult(result, meta) {
|
||||
const stdout = result.stdout.trim();
|
||||
const stderr = result.stderr.trim();
|
||||
const lines = [
|
||||
`# Codex ${meta.reviewLabel}`,
|
||||
"",
|
||||
`Target: ${meta.targetLabel}`,
|
||||
""
|
||||
];
|
||||
|
||||
if (stdout) {
|
||||
lines.push(stdout);
|
||||
} else if (result.status === 0) {
|
||||
lines.push("Codex review completed without any stdout output.");
|
||||
} else {
|
||||
lines.push("Codex review failed.");
|
||||
}
|
||||
|
||||
if (stderr) {
|
||||
lines.push("", "stderr:", "", "```text", stderr, "```");
|
||||
}
|
||||
|
||||
appendReasoningSection(lines, meta.reasoningSummary);
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
|
||||
export function renderTaskResult(parsedResult, meta) {
|
||||
const rawOutput = typeof parsedResult?.rawOutput === "string" ? parsedResult.rawOutput : "";
|
||||
if (rawOutput) {
|
||||
return rawOutput.endsWith("\n") ? rawOutput : `${rawOutput}\n`;
|
||||
}
|
||||
|
||||
const message = String(parsedResult?.failureMessage ?? "").trim() || "Codex did not return a final message.";
|
||||
return `${message}\n`;
|
||||
}
|
||||
|
||||
export function renderStatusReport(report) {
|
||||
const lines = [
|
||||
"# Codex Status",
|
||||
"",
|
||||
`Session runtime: ${report.sessionRuntime.label}`,
|
||||
`Review gate: ${report.config.stopReviewGate ? "enabled" : "disabled"}`,
|
||||
""
|
||||
];
|
||||
|
||||
if (report.running.length > 0) {
|
||||
appendActiveJobsTable(lines, report.running);
|
||||
lines.push("");
|
||||
lines.push("Live details:");
|
||||
for (const job of report.running) {
|
||||
pushJobDetails(lines, job, {
|
||||
showElapsed: true,
|
||||
showLog: true
|
||||
});
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
if (report.latestFinished) {
|
||||
lines.push("Latest finished:");
|
||||
pushJobDetails(lines, report.latestFinished, {
|
||||
showDuration: true,
|
||||
showLog: report.latestFinished.status === "failed"
|
||||
});
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
if (report.recent.length > 0) {
|
||||
lines.push("Recent jobs:");
|
||||
for (const job of report.recent) {
|
||||
pushJobDetails(lines, job, {
|
||||
showDuration: true,
|
||||
showLog: job.status === "failed"
|
||||
});
|
||||
}
|
||||
lines.push("");
|
||||
} else if (report.running.length === 0 && !report.latestFinished) {
|
||||
lines.push("No jobs recorded yet.", "");
|
||||
}
|
||||
|
||||
if (report.needsReview) {
|
||||
lines.push("The stop-time review gate is enabled.");
|
||||
lines.push("Ending the session will trigger a fresh Codex adversarial review and block if it finds issues.");
|
||||
}
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
|
||||
export function renderJobStatusReport(job) {
|
||||
const lines = ["# Codex Job Status", ""];
|
||||
pushJobDetails(lines, job, {
|
||||
showElapsed: job.status === "queued" || job.status === "running",
|
||||
showDuration: job.status !== "queued" && job.status !== "running",
|
||||
showLog: true,
|
||||
showCancelHint: true,
|
||||
showResultHint: true,
|
||||
showReviewHint: true
|
||||
});
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
|
||||
export function renderStoredJobResult(job, storedJob) {
|
||||
const threadId = storedJob?.threadId ?? job.threadId ?? null;
|
||||
const resumeCommand = threadId ? `codex resume ${threadId}` : null;
|
||||
if (isStructuredReviewStoredResult(storedJob) && storedJob?.rendered) {
|
||||
const output = storedJob.rendered.endsWith("\n") ? storedJob.rendered : `${storedJob.rendered}\n`;
|
||||
if (!threadId) {
|
||||
return output;
|
||||
}
|
||||
return `${output}\nCodex session ID: ${threadId}\nResume in Codex: ${resumeCommand}\n`;
|
||||
}
|
||||
|
||||
const rawOutput =
|
||||
(typeof storedJob?.result?.rawOutput === "string" && storedJob.result.rawOutput) ||
|
||||
(typeof storedJob?.result?.codex?.stdout === "string" && storedJob.result.codex.stdout) ||
|
||||
"";
|
||||
if (rawOutput) {
|
||||
const output = rawOutput.endsWith("\n") ? rawOutput : `${rawOutput}\n`;
|
||||
if (!threadId) {
|
||||
return output;
|
||||
}
|
||||
return `${output}\nCodex session ID: ${threadId}\nResume in Codex: ${resumeCommand}\n`;
|
||||
}
|
||||
|
||||
if (storedJob?.rendered) {
|
||||
const output = storedJob.rendered.endsWith("\n") ? storedJob.rendered : `${storedJob.rendered}\n`;
|
||||
if (!threadId) {
|
||||
return output;
|
||||
}
|
||||
return `${output}\nCodex session ID: ${threadId}\nResume in Codex: ${resumeCommand}\n`;
|
||||
}
|
||||
|
||||
const lines = [
|
||||
`# ${job.title ?? "Codex Result"}`,
|
||||
"",
|
||||
`Job: ${job.id}`,
|
||||
`Status: ${job.status}`
|
||||
];
|
||||
|
||||
if (threadId) {
|
||||
lines.push(`Codex session ID: ${threadId}`);
|
||||
lines.push(`Resume in Codex: ${resumeCommand}`);
|
||||
}
|
||||
|
||||
if (job.summary) {
|
||||
lines.push(`Summary: ${job.summary}`);
|
||||
}
|
||||
|
||||
if (job.errorMessage) {
|
||||
lines.push("", job.errorMessage);
|
||||
} else if (storedJob?.errorMessage) {
|
||||
lines.push("", storedJob.errorMessage);
|
||||
} else {
|
||||
lines.push("", "No captured result payload was stored for this job.");
|
||||
}
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
|
||||
export function renderCancelReport(job) {
|
||||
const lines = [
|
||||
"# Codex Cancel",
|
||||
"",
|
||||
`Cancelled ${job.id}.`,
|
||||
""
|
||||
];
|
||||
|
||||
if (job.title) {
|
||||
lines.push(`- Title: ${job.title}`);
|
||||
}
|
||||
if (job.summary) {
|
||||
lines.push(`- Summary: ${job.summary}`);
|
||||
}
|
||||
lines.push("- Check `/codex:status` for the updated queue.");
|
||||
|
||||
return `${lines.join("\n").trimEnd()}\n`;
|
||||
}
|
||||
Reference in New Issue
Block a user