mirror of
https://github.com/run-llama/agentfs-claude.git
synced 2026-07-01 21:24:01 -04:00
wip: add codex (untested)
This commit is contained in:
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user