Skip to content

Commit 6dae7c8

Browse files
author
Frank
committed
Converted agents to Agent namespace. Change loggingPrefix to logger convention
1 parent 12be551 commit 6dae7c8

File tree

13 files changed

+546
-295
lines changed

13 files changed

+546
-295
lines changed

agents/claude-code.ts

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ import process from "node:process";
33

44
import { query } from "@anthropic-ai/claude-agent-sdk";
55

6-
import type {
7-
AgentDefinition,
8-
AgentRunOptions,
9-
AgentRunResult,
10-
} from "~/lib/createAgent.js";
6+
import type { Agent } from "~/agents/index.js";
7+
import { Logger } from "~/lib/logger.js";
118

129
const sessionCache = new Map<string, string>();
1310

@@ -38,37 +35,33 @@ function formatCommand(command: string, args: string[]): string {
3835
function writeLog(
3936
output: NodeJS.WriteStream,
4037
message: string,
41-
prefix: string | undefined,
38+
logger?: Logger.Instance,
4239
): void {
43-
if (prefix) {
44-
output.write(`[${prefix}] ${message}\n`);
45-
} else {
46-
output.write(`${message}\n`);
47-
}
40+
output.write(`${logger?.format(message) ?? message}\n`);
4841
}
4942

50-
function logJson(value: unknown, options: AgentRunOptions | undefined): void {
43+
function logJson(value: unknown, options: Agent.RunOptions): void {
5144
try {
52-
writeLog(process.stdout, JSON.stringify(value), options?.logPrefix);
45+
writeLog(process.stdout, JSON.stringify(value), options.logger);
5346
} catch (error) {
5447
const reason = error instanceof Error ? error.message : String(error);
5548
writeLog(
5649
process.stdout,
5750
JSON.stringify({ error: "serialization_failed", reason }),
58-
options?.logPrefix,
51+
options.logger,
5952
);
6053
}
6154
}
6255

63-
function logError(value: unknown, options: AgentRunOptions | undefined): void {
56+
function logError(value: unknown, options: Agent.RunOptions): void {
6457
try {
65-
writeLog(process.stderr, JSON.stringify(value), options?.logPrefix);
58+
writeLog(process.stderr, JSON.stringify(value), options.logger);
6659
} catch (error) {
6760
const reason = error instanceof Error ? error.message : String(error);
6861
writeLog(
6962
process.stderr,
7063
JSON.stringify({ error: "serialization_failed", reason }),
71-
options?.logPrefix,
64+
options.logger,
7265
);
7366
}
7467
}
@@ -84,13 +77,8 @@ function serializeError(error: unknown): Record<string, unknown> {
8477
return { value: String(error) };
8578
}
8679

87-
const claudeCodeAgent: AgentDefinition = {
88-
async run(
89-
model: string,
90-
prompt: string,
91-
cwd: string,
92-
options?: AgentRunOptions,
93-
): Promise<AgentRunResult> {
80+
const claudeCodeAgent: Agent.Definition = {
81+
async run(model, prompt, cwd, options) {
9482
assert(
9583
typeof prompt === "string",
9684
"Claude Code agent requires a prompt string.",
@@ -102,17 +90,17 @@ const claudeCodeAgent: AgentDefinition = {
10290
prompt,
10391
]);
10492

105-
options?.onStart?.(displayCommand);
93+
options.logger.log(displayCommand);
10694

10795
const cacheKey = sessionKey(cwd, model);
10896
const existingSessionID = sessionCache.get(cacheKey);
10997

110-
const actions: string[] = [];
111-
const usage = {
112-
input: 0,
113-
output: 0,
114-
cost: 0,
115-
};
98+
const actions: string[] = [];
99+
const usage = {
100+
input: 0,
101+
output: 0,
102+
cost: 0,
103+
};
116104

117105
try {
118106
const result = query({

agents/codex.ts

Lines changed: 21 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,8 @@ import {
1010
type ThreadItem,
1111
} from "@openai/codex-sdk";
1212

13-
import type {
14-
AgentDefinition,
15-
AgentRunOptions,
16-
AgentRunResult,
17-
} from "~/lib/createAgent.js";
13+
import type { Agent } from "~/agents/index.js";
14+
import { Logger } from "~/lib/logger.js";
1815

1916
const DEFAULT_SANDBOX: SandboxMode = "workspace-write";
2017

@@ -48,13 +45,9 @@ function formatCommand(command: string, args: string[]): string {
4845
function writeLog(
4946
output: NodeJS.WriteStream,
5047
message: string,
51-
prefix: string | undefined,
48+
logger?: Logger.Instance,
5249
): void {
53-
if (prefix) {
54-
output.write(`[${prefix}] ${message}\n`);
55-
} else {
56-
output.write(`${message}\n`);
57-
}
50+
output.write(`${logger?.format(message) ?? message}\n`);
5851
}
5952

6053
function isCommandExecutionItem(
@@ -63,27 +56,20 @@ function isCommandExecutionItem(
6356
return item.type === "command_execution";
6457
}
6558

66-
function logTurnItems(
67-
items: ThreadItem[],
68-
options: AgentRunOptions | undefined,
69-
): void {
59+
function logTurnItems(items: ThreadItem[], options: Agent.RunOptions): void {
7060
for (const item of items) {
7161
try {
72-
writeLog(process.stdout, JSON.stringify(item), options?.logPrefix);
62+
writeLog(process.stdout, JSON.stringify(item), options.logger);
7363
} catch (error) {
7464
const sanitizedItem = isCommandExecutionItem(item)
7565
? { ...item, aggregated_output: "<omitted>" }
7666
: item;
77-
writeLog(
78-
process.stdout,
79-
JSON.stringify(sanitizedItem),
80-
options?.logPrefix,
81-
);
67+
writeLog(process.stdout, JSON.stringify(sanitizedItem), options.logger);
8268
if (error instanceof Error) {
8369
writeLog(
8470
process.stderr,
8571
`Failed to serialize Codex item: ${error.message}`,
86-
options?.logPrefix,
72+
options.logger,
8773
);
8874
}
8975
}
@@ -106,13 +92,8 @@ function getOrCreateThread(model: string, cwd: string): Thread {
10692
return thread;
10793
}
10894

109-
const codexAgent: AgentDefinition<(typeof models)[number]> = {
110-
async run(
111-
model: (typeof models)[number],
112-
prompt: string,
113-
cwd: string,
114-
options?: AgentRunOptions,
115-
): Promise<AgentRunResult> {
95+
const codexAgent: Agent.Definition<(typeof models)[number]> = {
96+
async run(model, prompt, cwd, options) {
11697
assert(typeof prompt === "string", "Codex agent requires a prompt string.");
11798

11899
const displayCommand = formatCommand("codex-sdk", [
@@ -123,7 +104,7 @@ const codexAgent: AgentDefinition<(typeof models)[number]> = {
123104
prompt,
124105
]);
125106

126-
options?.onStart?.(displayCommand);
107+
options.logger.log(displayCommand);
127108

128109
const key = sessionKey(model, cwd);
129110
const thread = getOrCreateThread(model, cwd);
@@ -168,18 +149,20 @@ const codexAgent: AgentDefinition<(typeof models)[number]> = {
168149

169150
export default codexAgent;
170151

171-
172152
const response = await fetch("https://models.dev/api.json");
173153
if (!response.ok) {
174154
throw new Error(`models.dev responded with ${response.status}`);
175155
}
176156

177157
const openai = (await response.json())["openai"] as {
178-
models: Record<string, {
179-
cost: {
180-
input: number,
181-
output: number,
182-
cache_read: number
158+
models: Record<
159+
string,
160+
{
161+
cost: {
162+
input: number;
163+
output: number;
164+
cache_read: number;
165+
};
183166
}
184-
}>
185-
}
167+
>;
168+
};

agents/index.ts

Lines changed: 73 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,83 @@
11
import { strict as assert } from "node:assert";
2+
import * as opencodeAgent from "~/agents/opencode.js";
3+
import * as codexAgent from "~/agents/codex.js";
4+
import * as claudeCodeAgent from "~/agents/claude-code.js";
5+
import { Logger } from "~/lib/logger.js";
26

3-
import type { AgentDefinition } from "~/lib/createAgent.js";
7+
export namespace Agent {
8+
export type Prompt = string;
49

5-
export interface AgentRegistration<TModel extends string = string> {
6-
name: string;
7-
definition: AgentDefinition<TModel>;
8-
models: ReadonlyArray<TModel>;
9-
}
10+
export type CommandSpec =
11+
| string
12+
| {
13+
command: string;
14+
args?: string[];
15+
shell?: boolean;
16+
};
1017

11-
interface AgentModuleShape<TModel extends string = string> {
12-
default?: AgentDefinition<TModel>;
13-
models?: ReadonlyArray<TModel>;
14-
}
18+
export type Executor = (
19+
model: string,
20+
prompt: Prompt,
21+
) => CommandSpec | Promise<CommandSpec>;
1522

16-
function createAgentRegistration<TModel extends string>(
17-
name: string,
18-
module: AgentModuleShape<TModel>,
19-
): AgentRegistration<TModel> {
20-
const definition = module.default;
21-
const models = module.models;
23+
export interface Definition<TModel extends string = string> {
24+
run: (
25+
model: TModel,
26+
prompt: Prompt,
27+
cwd: string,
28+
options: RunOptions,
29+
) => Promise<RunResult>;
30+
cleanup?: () => void | Promise<void>;
31+
}
2232

23-
assert(definition, `Agent module ${name} is missing a default export.`);
24-
assert(models, `Agent module ${name} is missing the exported models list.`);
33+
export interface RunResult {
34+
command: string;
35+
actions: string[];
36+
usage: {
37+
input: number;
38+
output: number;
39+
cost: number;
40+
};
41+
}
2542

26-
return { name, definition, models };
27-
}
43+
export interface RunOptions {
44+
logger: Logger.Instance;
45+
}
2846

29-
const agents: Record<string, AgentRegistration<any>> = {
30-
// Only keep opencode active while debugging timeouts for specific models.
31-
opencode: createAgentRegistration(
32-
"opencode",
33-
await import("~/agents/opencode.js"),
34-
),
35-
// codex: createAgentRegistration("codex", await import("~/agents/codex.js")),
36-
// "claude-code": createAgentRegistration(
37-
// "claude-code",
38-
// await import("~/agents/claude-code.js"),
39-
// ),
40-
};
41-
42-
export async function getAgent(
43-
name: string,
44-
): Promise<AgentRegistration | undefined> {
45-
return agents[name];
46-
}
47+
export interface Registration<TModel extends string = string> {
48+
name: string;
49+
definition: Definition<TModel>;
50+
models: ReadonlyArray<TModel>;
51+
}
52+
53+
const agents: Record<string, Registration<any>> = {
54+
// Only keep opencode active while debugging timeouts for specific models.
55+
opencode: createRegistration("opencode", opencodeAgent),
56+
//codex: createRegistration("codex", codexAgent),
57+
//"claude-code": createRegistration("claude-code", claudeCodeAgent),
58+
};
59+
60+
function createRegistration<TModel extends string>(
61+
name: string,
62+
module: {
63+
default?: Definition<TModel>;
64+
models?: ReadonlyArray<TModel>;
65+
},
66+
): Registration<TModel> {
67+
const definition = module.default;
68+
const models = module.models;
69+
70+
assert(definition, `Agent module ${name} is missing a default export.`);
71+
assert(models, `Agent module ${name} is missing the exported models list.`);
72+
73+
return { name, definition, models };
74+
}
75+
76+
export function get(name: string): Registration | undefined {
77+
return agents[name];
78+
}
4779

48-
export function listAgents() {
49-
return Object.values(agents);
80+
export function list() {
81+
return Object.values(agents);
82+
}
5083
}

0 commit comments

Comments
 (0)