81 lines
2.3 KiB
TypeScript
81 lines
2.3 KiB
TypeScript
import path from "node:path";
|
|
|
|
import type { AgentMeta, AgentParseResult, AgentSpec } from "./types.js";
|
|
|
|
function parseCodexJson(raw: string): AgentParseResult {
|
|
const lines = raw.split(/\n+/).filter((l) => l.trim().startsWith("{"));
|
|
const texts: string[] = [];
|
|
let meta: AgentMeta | undefined;
|
|
|
|
for (const line of lines) {
|
|
try {
|
|
const ev = JSON.parse(line) as {
|
|
type?: string;
|
|
item?: { type?: string; text?: string };
|
|
usage?: unknown;
|
|
};
|
|
// Codex streams multiple events; capture the last agent_message text and
|
|
// the final turn usage for cost/telemetry.
|
|
if (
|
|
ev.type === "item.completed" &&
|
|
ev.item?.type === "agent_message" &&
|
|
typeof ev.item.text === "string"
|
|
) {
|
|
texts.push(ev.item.text);
|
|
}
|
|
if (
|
|
ev.type === "turn.completed" &&
|
|
ev.usage &&
|
|
typeof ev.usage === "object"
|
|
) {
|
|
const u = ev.usage as {
|
|
input_tokens?: number;
|
|
cached_input_tokens?: number;
|
|
output_tokens?: number;
|
|
};
|
|
meta = {
|
|
usage: {
|
|
input: u.input_tokens,
|
|
output: u.output_tokens,
|
|
cacheRead: u.cached_input_tokens,
|
|
total:
|
|
(u.input_tokens ?? 0) +
|
|
(u.output_tokens ?? 0) +
|
|
(u.cached_input_tokens ?? 0),
|
|
},
|
|
};
|
|
}
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
const finalTexts = texts.length ? texts.map((t) => t.trim()) : undefined;
|
|
return { texts: finalTexts, meta };
|
|
}
|
|
|
|
export const codexSpec: AgentSpec = {
|
|
kind: "codex",
|
|
isInvocation: (argv) => argv.length > 0 && path.basename(argv[0]) === "codex",
|
|
buildArgs: (ctx) => {
|
|
const argv = [...ctx.argv];
|
|
const hasExec = argv.length > 0 && argv[1] === "exec";
|
|
if (!hasExec) {
|
|
argv.splice(1, 0, "exec");
|
|
}
|
|
// Ensure JSON output
|
|
if (!argv.includes("--json")) {
|
|
argv.splice(argv.length - 1, 0, "--json");
|
|
}
|
|
// Safety defaults
|
|
if (!argv.includes("--skip-git-repo-check")) {
|
|
argv.splice(argv.length - 1, 0, "--skip-git-repo-check");
|
|
}
|
|
if (!argv.some((p) => p === "--sandbox" || p.startsWith("--sandbox="))) {
|
|
argv.splice(argv.length - 1, 0, "--sandbox", "read-only");
|
|
}
|
|
return argv;
|
|
},
|
|
parseOutput: parseCodexJson,
|
|
};
|