wip: add codex (untested)

This commit is contained in:
Clelia (Astra) Bertelli
2025-12-18 15:33:18 +01:00
parent 1c1217e35b
commit eac2f55cbc
3 changed files with 297 additions and 14 deletions
+1
View File
@@ -38,6 +38,7 @@
"@anthropic-ai/claude-agent-sdk": "^0.1.59",
"@llamaindex/workflow-core": "^1.3.3",
"@modelcontextprotocol/sdk": "^1.24.3",
"@openai/codex-sdk": "^0.73.0",
"@visulima/colorize": "^1.4.29",
"agentfs-sdk": "^0.2.1",
"figlet": "^1.9.4",
+263 -1
View File
@@ -1 +1,263 @@
// take inspiration from https://github.com/openai/codex/blob/main/sdk/typescript/samples/basic_streaming.ts
import { Codex } from "@openai/codex-sdk";
import type { ThreadEvent, Thread } from "@openai/codex-sdk";
import {
red,
green,
magentaBright,
yellow,
bold,
cyan,
} from "@visulima/colorize";
export const codex = new Codex({});
export async function runCodex(
prompt: string,
{ resumeSession = undefined }: { resumeSession: string | undefined },
) {
let thread: Thread | undefined = undefined;
if (typeof resumeSession == "undefined") {
thread = codex.startThread({
skipGitRepoCheck: true,
});
} else {
thread = codex.resumeThread(resumeSession, {
skipGitRepoCheck: true,
});
}
const { events } = await thread.runStreamed(prompt);
for await (const event of events) {
switch (event.type) {
case "item.started":
await handleItemStart(event);
break;
case "item.updated":
await handleItemUpdated(event);
break;
case "item.completed":
await handleItemCompleted(event);
break;
case "turn.completed":
await handleTurnCompletion(event);
break;
case "error":
await handleError(event);
break;
}
}
}
async function handleItemStart(event: ThreadEvent) {
if (event.type == "item.started") {
if (event.item.type == "agent_message") {
console.log(bold(magentaBright("Assistant started responding...")));
console.log(event.item.text);
} else if (event.item.type == "reasoning") {
console.log(bold(magentaBright("Assistant started thinking...")));
console.log(event.item.text);
} else if (event.item.type == "mcp_tool_call") {
console.log(
bold(
yellow(
`Assistant started calling MCP tool ${event.item.tool} with input ${JSON.stringify(event.item.arguments)}`,
),
),
);
if (typeof event.item.error != "undefined") {
console.log(
red(
bold(
`An error occurred while calling the tool: ${event.item.error.message}`,
),
),
);
} else {
if (typeof event.item.result != "undefined") {
let finalResult = "";
for (const block of event.item.result.content) {
if (block.type == "text") {
finalResult += block.text + "\n";
}
}
console.log(`${green(bold("Tool result"))}: ${finalResult}`);
}
}
} else if (event.item.type == "todo_list") {
console.log(
bold(green("Assistant started to produce a TODO list with items:")),
);
let c = 0;
for (const i of event.item.items) {
c += 1;
console.log(
`TODO item ${c}: ${i.text} (${i.completed ? "completed" : "not completed"})`,
);
}
} else if (event.item.type == "web_search") {
console.log(
`Assistant started searching the web with query: ${event.item.query}`,
);
} else if (event.item.type == "command_execution") {
console.log(
`Assistant started to execute command: ${event.item.command}`,
);
} else if (event.item.type == "file_change") {
console.log(
bold(red("WARNING! The assistant is starting to change files:")),
);
for (const change of event.item.changes) {
console.log(
`The assistant would like to apply ${change.kind} to ${change.path}`,
);
}
} else {
console.log(bold(red(`An error occurred: ${event.item.message}`)));
}
}
}
async function handleItemUpdated(event: ThreadEvent) {
if (event.type == "item.started") {
if (event.item.type == "agent_message") {
console.log(bold(magentaBright("Assistant updated its response...")));
console.log(event.item.text);
} else if (event.item.type == "reasoning") {
console.log(bold(magentaBright("Assistant updated its thoughts...")));
console.log(event.item.text);
} else if (event.item.type == "mcp_tool_call") {
console.log(
bold(
yellow(
`Assistant updated its call to MCP tool ${event.item.tool} with input ${JSON.stringify(event.item.arguments)}`,
),
),
);
if (typeof event.item.error != "undefined") {
console.log(
red(
bold(
`An error occurred while calling the tool: ${event.item.error.message}`,
),
),
);
} else {
if (typeof event.item.result != "undefined") {
let finalResult = "";
for (const block of event.item.result.content) {
if (block.type == "text") {
finalResult += block.text + "\n";
}
}
console.log(`${green(bold("Tool result"))}: ${finalResult}`);
}
}
} else if (event.item.type == "todo_list") {
console.log(bold(green("Assistant updated its TODO list with items:")));
let c = 0;
for (const i of event.item.items) {
c += 1;
console.log(
`TODO item ${c}: ${i.text} (${i.completed ? "completed" : "not completed"})`,
);
}
} else if (event.item.type == "web_search") {
console.log(
`Assistant updated its web search with query: ${event.item.query}`,
);
} else if (event.item.type == "command_execution") {
console.log(`Assistant updated command execution: ${event.item.command}`);
} else if (event.item.type == "file_change") {
console.log(
bold(red("WARNING! The assistant is starting to change files:")),
);
for (const change of event.item.changes) {
console.log(
`The assistant is updating the change it would like to apply (${change.kind}) to ${change.path}`,
);
}
} else {
console.log(bold(red(`An error occurred: ${event.item.message}`)));
}
}
}
async function handleItemCompleted(event: ThreadEvent) {
if (event.type == "item.completed") {
if (event.item.type == "agent_message") {
console.log(bold(magentaBright("Assistant completed its response...")));
console.log(event.item.text);
} else if (event.item.type == "reasoning") {
console.log(bold(magentaBright("Assistant completed its thoughts...")));
console.log(event.item.text);
} else if (event.item.type == "mcp_tool_call") {
console.log(
bold(
yellow(
`Assistant completed its call to MCP tool ${event.item.tool} with input ${JSON.stringify(event.item.arguments)}`,
),
),
);
if (typeof event.item.error != "undefined") {
console.log(
red(
bold(
`An error occurred while calling the tool: ${event.item.error.message}`,
),
),
);
} else {
if (typeof event.item.result != "undefined") {
let finalResult = "";
for (const block of event.item.result.content) {
if (block.type == "text") {
finalResult += block.text + "\n";
}
}
console.log(`${green(bold("Tool result"))}: ${finalResult}`);
}
}
} else if (event.item.type == "todo_list") {
console.log(bold(green("Assistant completed its TODO list with items:")));
let c = 0;
for (const i of event.item.items) {
c += 1;
console.log(
`TODO item ${c}: ${i.text} (${i.completed ? "completed" : "not completed"})`,
);
}
} else if (event.item.type == "web_search") {
console.log(
`Assistant updated its web search with query: ${event.item.query}`,
);
} else if (event.item.type == "command_execution") {
console.log(`Assistant updated command execution: ${event.item.command}`);
} else if (event.item.type == "file_change") {
console.log(
bold(red("ERROR! The assistant has completed its file changes:")),
);
for (const change of event.item.changes) {
console.log(
`The assistant has completed the change it applied to ${change.path} (${change.kind})`,
);
}
} else {
console.log(bold(red(`An error occurred: ${event.item.message}`)));
}
}
}
async function handleTurnCompletion(event: ThreadEvent) {
if (event.type == "turn.completed") {
console.log(cyan(bold("Turn completed, usage:")));
console.log(`Input tokens: ${event.usage.input_tokens}`);
console.log(`Cached input tokens: ${event.usage.cached_input_tokens}`);
console.log(`Output tokens: ${event.usage.output_tokens}`);
}
}
async function handleError(event: ThreadEvent) {
if (event.type == "error") {
console.log(bold(red(`An error occurred: ${event.message}`)));
}
}
+33 -13
View File
@@ -7,6 +7,7 @@ import { queryOptions } from "./options";
import { bold } from "@visulima/colorize";
import { consoleInput, renderLogo } from "./cli";
import * as fs from "fs";
import { runCodex } from "./codex";
async function main() {
const { withState } = createStatefulMiddleware(() => ({}));
@@ -15,6 +16,7 @@ async function main() {
const filesRegisteredEvent = workflowEvent<void>();
const requestPromptEvent = workflowEvent<void>();
const promptEvent = workflowEvent<{
chosenAgent: string;
prompt: string;
resume: string | undefined;
plan: boolean;
@@ -59,15 +61,23 @@ async function main() {
workflow.handle([promptEvent], async (_context, event) => {
const prompt = event.data.prompt;
const agent = new Agent(queryOptions, {
resume: event.data.resume,
plan: event.data.plan,
});
try {
await agent.run(prompt);
return stopEvent.with({ success: true, error: null });
} catch (error) {
return stopEvent.with({ success: false, error: JSON.stringify(error) });
if (event.data.chosenAgent == "claude") {
const agent = new Agent(queryOptions, {
resume: event.data.resume,
plan: event.data.plan,
});
try {
await agent.run(prompt);
return stopEvent.with({ success: true, error: null });
} catch (error) {
return stopEvent.with({ success: false, error: JSON.stringify(error) });
}
} else {
try {
await runCodex(event.data.prompt, { resumeSession: event.data.resume });
} catch (error) {
return stopEvent.with({ success: false, error: JSON.stringify(error) });
}
}
});
@@ -75,6 +85,13 @@ async function main() {
sendEvent(startEvent.with({ workingDirectory: "./" }));
await stream.until(requestPromptEvent).toArray();
const snapshotData = await snapshot();
let agentOfChoice = "claude";
const chosenAgent = await consoleInput(
"What agent would you like to use? [codex/claude]",
);
if (chosenAgent.trim() != "") {
agentOfChoice = chosenAgent;
}
const humanResponse = await consoleInput("Your prompt: ");
console.log(
bold("Would you like to resume a previous session? Leave blank if not"),
@@ -84,15 +101,18 @@ async function main() {
if (resumeSession.trim() != "") {
sessionId = resumeSession;
}
console.log(bold("Would you like to activate plan mode? [y/n]"));
const activatePlan = await consoleInput("Your answer: ");
let planMode = false;
if (["yes", "y", "yse"].includes(activatePlan.trim().toLowerCase())) {
planMode = true;
if (agentOfChoice == "claude") {
console.log(bold("Would you like to activate plan mode? [y/n]"));
const activatePlan = await consoleInput("Your answer: ");
if (["yes", "y", "yse"].includes(activatePlan.trim().toLowerCase())) {
planMode = true;
}
}
const resumedContext = workflow.resume(snapshotData);
resumedContext.sendEvent(
promptEvent.with({
chosenAgent: agentOfChoice,
prompt: humanResponse,
resume: sessionId,
plan: planMode,