@messenger-agent/codex-agent 0.24.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-server-client.d.ts +32 -0
- package/dist/app-server-client.js +316 -0
- package/dist/app-server-protocol.d.ts +325 -0
- package/dist/app-server-protocol.js +1 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.js +15 -0
- package/dist/assets/codex-home/AGENTS.md +50 -0
- package/dist/codex.d.ts +63 -0
- package/dist/codex.js +461 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.js +154 -0
- package/dist/db.d.ts +10 -0
- package/dist/db.js +32 -0
- package/dist/dynamic-tools.d.ts +14 -0
- package/dist/dynamic-tools.js +172 -0
- package/dist/git-init.d.ts +1 -0
- package/dist/git-init.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +19 -0
- package/dist/platform-instructions.d.ts +2 -0
- package/dist/platform-instructions.js +21 -0
- package/dist/prompts/plan-mode-system-reminder.txt +26 -0
- package/dist/prompts/question-tool-description.txt +12 -0
- package/dist/routes/chat.d.ts +27 -0
- package/dist/routes/chat.js +909 -0
- package/dist/routes/shared.d.ts +37 -0
- package/dist/routes/shared.js +218 -0
- package/dist/schemas.d.ts +112 -0
- package/dist/schemas.js +72 -0
- package/dist/tunnel-client.d.ts +15 -0
- package/dist/tunnel-client.js +232 -0
- package/package.json +36 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { parse } from "yaml";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { normalizeOptional, parseAgentAuthConfig, parseAgentWorkspacesConfig, parseGitInitConfig, } from "@messenger-agent/shared/agent-config";
|
|
7
|
+
import { logger, normalizeLogLevel, setLogFile, setLogLevel } from "@messenger-agent/shared/logger";
|
|
8
|
+
const defaultTunnelServerUrl = "wss://m.elevo.vip/agent-bridge/tunnel";
|
|
9
|
+
const defaultCodexBaseUrl = "https://m.elevo.vip/agent-bridge/llm/v1";
|
|
10
|
+
const RawConfigSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
log_level: z.string().optional(),
|
|
13
|
+
port: z.coerce.number().int().positive().optional(),
|
|
14
|
+
auth_tokens: z.record(z.string(), z.string()).optional(),
|
|
15
|
+
workspaces: z
|
|
16
|
+
.array(z.object({
|
|
17
|
+
id: z.string().optional(),
|
|
18
|
+
name: z.string().optional(),
|
|
19
|
+
path: z.string().optional(),
|
|
20
|
+
}))
|
|
21
|
+
.optional(),
|
|
22
|
+
file_uploads: z
|
|
23
|
+
.object({
|
|
24
|
+
temp_dir: z.string().optional(),
|
|
25
|
+
})
|
|
26
|
+
.optional(),
|
|
27
|
+
data_dir: z.string().optional(),
|
|
28
|
+
codex: z
|
|
29
|
+
.object({
|
|
30
|
+
port: z.coerce.number().int().positive().optional(),
|
|
31
|
+
data_dir: z.string().optional(),
|
|
32
|
+
file_uploads: z.object({ temp_dir: z.string().optional() }).optional(),
|
|
33
|
+
api_key: z.string().optional(),
|
|
34
|
+
base_url: z.string().optional(),
|
|
35
|
+
})
|
|
36
|
+
.optional(),
|
|
37
|
+
openai: z
|
|
38
|
+
.object({
|
|
39
|
+
api_key: z.string().optional(),
|
|
40
|
+
base_url: z.string().optional(),
|
|
41
|
+
image_model: z.string().optional(),
|
|
42
|
+
})
|
|
43
|
+
.optional(),
|
|
44
|
+
gitlab: z
|
|
45
|
+
.object({
|
|
46
|
+
ca_cert_path: z.string().optional(),
|
|
47
|
+
host: z.string().optional(),
|
|
48
|
+
})
|
|
49
|
+
.optional(),
|
|
50
|
+
git: z
|
|
51
|
+
.object({
|
|
52
|
+
co_authors: z.array(z.string()).optional(),
|
|
53
|
+
})
|
|
54
|
+
.optional(),
|
|
55
|
+
tunnel: z
|
|
56
|
+
.object({
|
|
57
|
+
enabled: z.boolean().optional(),
|
|
58
|
+
server_url: z.string().optional(),
|
|
59
|
+
tunnel_id: z.string().optional(),
|
|
60
|
+
token: z.string().optional(),
|
|
61
|
+
reconnect_initial_ms: z.number().int().positive().optional(),
|
|
62
|
+
reconnect_max_ms: z.number().int().positive().optional(),
|
|
63
|
+
heartbeat_interval_ms: z.number().int().positive().optional(),
|
|
64
|
+
})
|
|
65
|
+
.optional(),
|
|
66
|
+
})
|
|
67
|
+
.loose();
|
|
68
|
+
function loadConfig() {
|
|
69
|
+
const configPath = process.env.AGENT_CONFIG_PATH ?? "./agent-config.yaml";
|
|
70
|
+
if (!existsSync(configPath)) {
|
|
71
|
+
const dataDir = "./data";
|
|
72
|
+
const logFile = join(dataDir, "logs", "codex-agent.log");
|
|
73
|
+
setLogLevel("info");
|
|
74
|
+
setLogFile(logFile);
|
|
75
|
+
logger.warn(`Config file not found at ${configPath}`);
|
|
76
|
+
return {
|
|
77
|
+
configPath,
|
|
78
|
+
logLevel: "info",
|
|
79
|
+
logFile,
|
|
80
|
+
port: 3000,
|
|
81
|
+
fileUploads: { tempDir: join(tmpdir(), "codex-agent-uploads") },
|
|
82
|
+
dataDir,
|
|
83
|
+
sessionsDbPath: join(dataDir, "sessions.db"),
|
|
84
|
+
codex: {},
|
|
85
|
+
openai: { imageModel: "gpt-image-2" },
|
|
86
|
+
tunnel: {
|
|
87
|
+
enabled: false,
|
|
88
|
+
reconnectInitialMs: 1000,
|
|
89
|
+
reconnectMaxMs: 30000,
|
|
90
|
+
heartbeatIntervalMs: 30000,
|
|
91
|
+
},
|
|
92
|
+
...parseAgentAuthConfig({}),
|
|
93
|
+
...parseAgentWorkspacesConfig({}),
|
|
94
|
+
...parseGitInitConfig({}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
let parsedYaml;
|
|
98
|
+
try {
|
|
99
|
+
parsedYaml = parse(readFileSync(configPath, "utf-8"));
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
logger.error(`Failed to parse config file at ${configPath}:`, err);
|
|
103
|
+
parsedYaml = {};
|
|
104
|
+
}
|
|
105
|
+
const raw = RawConfigSchema.safeParse(parsedYaml ?? {});
|
|
106
|
+
if (!raw.success) {
|
|
107
|
+
logger.error(`Invalid config format at ${configPath}:`, raw.error.issues);
|
|
108
|
+
}
|
|
109
|
+
const data = raw.success ? raw.data : {};
|
|
110
|
+
const logLevel = normalizeLogLevel(data.log_level);
|
|
111
|
+
const dataDir = normalizeOptional(data.codex?.data_dir) ?? normalizeOptional(data.data_dir) ?? "./data";
|
|
112
|
+
const logFile = join(dataDir, "logs", "codex-agent.log");
|
|
113
|
+
const tunnelEnabled = data.tunnel?.enabled ?? false;
|
|
114
|
+
const tunnelToken = normalizeOptional(data.tunnel?.token);
|
|
115
|
+
setLogLevel(logLevel);
|
|
116
|
+
setLogFile(logFile);
|
|
117
|
+
const config = {
|
|
118
|
+
configPath,
|
|
119
|
+
logLevel,
|
|
120
|
+
logFile,
|
|
121
|
+
port: data.codex?.port ?? data.port ?? 3000,
|
|
122
|
+
fileUploads: {
|
|
123
|
+
tempDir: normalizeOptional(data.codex?.file_uploads?.temp_dir) ??
|
|
124
|
+
normalizeOptional(data.file_uploads?.temp_dir) ??
|
|
125
|
+
join(tmpdir(), "codex-agent-uploads"),
|
|
126
|
+
},
|
|
127
|
+
dataDir,
|
|
128
|
+
sessionsDbPath: join(dataDir, "sessions.db"),
|
|
129
|
+
codex: {
|
|
130
|
+
apiKey: normalizeOptional(data.codex?.api_key) ?? (tunnelEnabled ? tunnelToken : undefined),
|
|
131
|
+
baseUrl: normalizeOptional(data.codex?.base_url) ?? (tunnelEnabled ? defaultCodexBaseUrl : undefined),
|
|
132
|
+
},
|
|
133
|
+
openai: {
|
|
134
|
+
apiKey: normalizeOptional(data.openai?.api_key) ?? (tunnelEnabled ? tunnelToken : undefined),
|
|
135
|
+
baseUrl: normalizeOptional(data.openai?.base_url) ?? (tunnelEnabled ? defaultCodexBaseUrl : undefined),
|
|
136
|
+
imageModel: normalizeOptional(data.openai?.image_model) ?? "gpt-image-2",
|
|
137
|
+
},
|
|
138
|
+
tunnel: {
|
|
139
|
+
enabled: tunnelEnabled,
|
|
140
|
+
serverUrl: normalizeOptional(data.tunnel?.server_url) ?? (tunnelEnabled ? defaultTunnelServerUrl : undefined),
|
|
141
|
+
tunnelId: normalizeOptional(data.tunnel?.tunnel_id),
|
|
142
|
+
token: tunnelToken,
|
|
143
|
+
reconnectInitialMs: data.tunnel?.reconnect_initial_ms ?? 1000,
|
|
144
|
+
reconnectMaxMs: data.tunnel?.reconnect_max_ms ?? 30000,
|
|
145
|
+
heartbeatIntervalMs: data.tunnel?.heartbeat_interval_ms ?? 30000,
|
|
146
|
+
},
|
|
147
|
+
...parseAgentAuthConfig(data),
|
|
148
|
+
...parseAgentWorkspacesConfig(data),
|
|
149
|
+
...parseGitInitConfig(data),
|
|
150
|
+
};
|
|
151
|
+
logger.info(`Loaded agent config from ${configPath}`);
|
|
152
|
+
return config;
|
|
153
|
+
}
|
|
154
|
+
export const appConfig = loadConfig();
|
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type SessionRow = {
|
|
2
|
+
sessionId: string;
|
|
3
|
+
workdir: string;
|
|
4
|
+
createdAt: number;
|
|
5
|
+
botName: string;
|
|
6
|
+
llmProxyBinding?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function persistSession(row: SessionRow): void;
|
|
9
|
+
export declare function loadAllSessions(): SessionRow[];
|
|
10
|
+
export declare function getSession(sessionId: string): SessionRow | undefined;
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { appConfig } from "./config.js";
|
|
5
|
+
const DB_PATH = appConfig.sessionsDbPath;
|
|
6
|
+
mkdirSync(dirname(DB_PATH), { recursive: true });
|
|
7
|
+
const db = new DatabaseSync(DB_PATH);
|
|
8
|
+
db.exec(`
|
|
9
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
10
|
+
sessionId TEXT PRIMARY KEY,
|
|
11
|
+
workdir TEXT NOT NULL,
|
|
12
|
+
createdAt INTEGER NOT NULL,
|
|
13
|
+
botName TEXT NOT NULL DEFAULT 'main',
|
|
14
|
+
llmProxyBinding TEXT
|
|
15
|
+
)
|
|
16
|
+
`);
|
|
17
|
+
const sessionColumns = db.prepare("PRAGMA table_info(sessions)").all();
|
|
18
|
+
if (!sessionColumns.some((column) => column.name === "llmProxyBinding")) {
|
|
19
|
+
db.exec("ALTER TABLE sessions ADD COLUMN llmProxyBinding TEXT");
|
|
20
|
+
}
|
|
21
|
+
const stmtInsert = db.prepare(`INSERT OR REPLACE INTO sessions (sessionId, workdir, createdAt, botName, llmProxyBinding) VALUES (?, ?, ?, ?, ?)`);
|
|
22
|
+
const stmtGetAll = db.prepare(`SELECT * FROM sessions`);
|
|
23
|
+
const stmtGet = db.prepare(`SELECT * FROM sessions WHERE sessionId = ?`);
|
|
24
|
+
export function persistSession(row) {
|
|
25
|
+
stmtInsert.run(row.sessionId, row.workdir, row.createdAt, row.botName, row.llmProxyBinding ?? null);
|
|
26
|
+
}
|
|
27
|
+
export function loadAllSessions() {
|
|
28
|
+
return stmtGetAll.all();
|
|
29
|
+
}
|
|
30
|
+
export function getSession(sessionId) {
|
|
31
|
+
return stmtGet.get(sessionId);
|
|
32
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { MatrixTaskEvent } from "@messenger-agent/shared/workspace-tasks";
|
|
2
|
+
import type { DynamicToolCallParams, DynamicToolCallResponse, DynamicToolSpec } from "./app-server-protocol.js";
|
|
3
|
+
type ManagedTaskToolContext = {
|
|
4
|
+
workspaceRoot: string;
|
|
5
|
+
threadRef?: string;
|
|
6
|
+
matrixEvent?: Omit<MatrixTaskEvent, "kind">;
|
|
7
|
+
llmProxyBinding?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function setManagedTaskToolContext(threadId: string, context: ManagedTaskToolContext): void;
|
|
10
|
+
export declare function inheritManagedTaskToolContext(parentThreadId: string, childThreadIds: string[]): void;
|
|
11
|
+
export declare function inheritManagedTaskToolContextFromNotification(method: string, params: unknown): void;
|
|
12
|
+
export declare function dynamicToolSpecs(): DynamicToolSpec[];
|
|
13
|
+
export declare function handleDynamicToolCall(params: DynamicToolCallParams): Promise<DynamicToolCallResponse>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { generateImage } from "ai";
|
|
4
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
5
|
+
import { Agent, fetch as undiciFetch } from "undici";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { executeManagedTaskTool, isManagedTaskToolName, MANAGED_TASK_TOOL_DEFINITIONS, } from "@messenger-agent/shared/managed-task-tools";
|
|
8
|
+
import { AgentFileError, resolveAgentFilePath } from "@messenger-agent/shared/agent-files";
|
|
9
|
+
const GENERATE_IMAGE_TIMEOUT_MS = 20 * 60 * 1000;
|
|
10
|
+
const generateImageDispatcher = new Agent({
|
|
11
|
+
headersTimeout: GENERATE_IMAGE_TIMEOUT_MS,
|
|
12
|
+
bodyTimeout: GENERATE_IMAGE_TIMEOUT_MS,
|
|
13
|
+
});
|
|
14
|
+
const generateImageFetch = (input, init) => undiciFetch(input, {
|
|
15
|
+
...init,
|
|
16
|
+
dispatcher: generateImageDispatcher,
|
|
17
|
+
});
|
|
18
|
+
const GenerateImageArgsSchema = z.object({
|
|
19
|
+
prompt: z.string().describe("The text prompt describing the image to generate"),
|
|
20
|
+
store_path: z
|
|
21
|
+
.string()
|
|
22
|
+
.describe("Absolute file path (including filename) to save the image. If omitted, saves to the current working directory with an auto-generated name.")
|
|
23
|
+
.optional(),
|
|
24
|
+
size: z
|
|
25
|
+
.enum(["1024x1024", "1536x1024", "1024x1536"])
|
|
26
|
+
.describe("Image size. Leave it undefined to use the auto size.")
|
|
27
|
+
.optional(),
|
|
28
|
+
});
|
|
29
|
+
const SendFileArgsSchema = z.object({
|
|
30
|
+
path: z
|
|
31
|
+
.string()
|
|
32
|
+
.describe("File to send. Absolute paths use the system path; relative paths resolve from the active workspace."),
|
|
33
|
+
});
|
|
34
|
+
const generateImageInputSchema = z.toJSONSchema(GenerateImageArgsSchema);
|
|
35
|
+
const sendFileInputSchema = z.toJSONSchema(SendFileArgsSchema);
|
|
36
|
+
function taskToolResponse(data) {
|
|
37
|
+
return { contentItems: [{ type: "inputText", text: JSON.stringify(data) }], success: true };
|
|
38
|
+
}
|
|
39
|
+
const taskToolContexts = new Map();
|
|
40
|
+
export function setManagedTaskToolContext(threadId, context) {
|
|
41
|
+
const threadRef = context.threadRef?.trim();
|
|
42
|
+
taskToolContexts.set(threadId, {
|
|
43
|
+
workspaceRoot: context.workspaceRoot,
|
|
44
|
+
...(threadRef ? { threadRef } : null),
|
|
45
|
+
...(context.matrixEvent ? { matrixEvent: context.matrixEvent } : null),
|
|
46
|
+
...(context.llmProxyBinding ? { llmProxyBinding: context.llmProxyBinding } : null),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export function inheritManagedTaskToolContext(parentThreadId, childThreadIds) {
|
|
50
|
+
const context = taskToolContexts.get(parentThreadId);
|
|
51
|
+
if (!context)
|
|
52
|
+
return;
|
|
53
|
+
for (const childThreadId of childThreadIds) {
|
|
54
|
+
if (childThreadId && childThreadId !== parentThreadId) {
|
|
55
|
+
setManagedTaskToolContext(childThreadId, context);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function inheritManagedTaskToolContextFromNotification(method, params) {
|
|
60
|
+
if (method !== "item/started" && method !== "item/completed")
|
|
61
|
+
return;
|
|
62
|
+
if (!params || typeof params !== "object")
|
|
63
|
+
return;
|
|
64
|
+
const record = params;
|
|
65
|
+
const item = record.item;
|
|
66
|
+
if (!item || typeof item !== "object")
|
|
67
|
+
return;
|
|
68
|
+
const itemRecord = item;
|
|
69
|
+
if (itemRecord.type === "subAgentActivity" &&
|
|
70
|
+
itemRecord.kind === "started" &&
|
|
71
|
+
typeof record.threadId === "string" &&
|
|
72
|
+
typeof itemRecord.agentThreadId === "string") {
|
|
73
|
+
inheritManagedTaskToolContext(record.threadId, [itemRecord.agentThreadId]);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (itemRecord.type === "collabAgentToolCall" &&
|
|
77
|
+
itemRecord.tool === "spawnAgent" &&
|
|
78
|
+
typeof itemRecord.senderThreadId === "string" &&
|
|
79
|
+
Array.isArray(itemRecord.receiverThreadIds)) {
|
|
80
|
+
inheritManagedTaskToolContext(itemRecord.senderThreadId, itemRecord.receiverThreadIds.filter((threadId) => typeof threadId === "string"));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function workspaceRootForTasks(threadId) {
|
|
84
|
+
const context = taskToolContexts.get(threadId);
|
|
85
|
+
if (context)
|
|
86
|
+
return context.workspaceRoot;
|
|
87
|
+
const { sessionManager } = await import("./codex.js");
|
|
88
|
+
const workdir = sessionManager.workdirFor(threadId);
|
|
89
|
+
if (!workdir) {
|
|
90
|
+
throw new Error(`No workspace context for thread ${threadId}; the session may not exist in this process`);
|
|
91
|
+
}
|
|
92
|
+
return workdir;
|
|
93
|
+
}
|
|
94
|
+
async function loadOpenAIConfig(threadId) {
|
|
95
|
+
const { appConfig } = await import("./config.js");
|
|
96
|
+
if (!appConfig.openai.apiKey) {
|
|
97
|
+
throw new Error("Missing openai.api_key in LLM config");
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
apiKey: appConfig.openai.apiKey,
|
|
101
|
+
baseURL: appConfig.openai.baseUrl,
|
|
102
|
+
imageModel: appConfig.openai.imageModel,
|
|
103
|
+
llmProxyBinding: taskToolContexts.get(threadId)?.llmProxyBinding,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export function dynamicToolSpecs() {
|
|
107
|
+
return [
|
|
108
|
+
{
|
|
109
|
+
name: "generate_image",
|
|
110
|
+
description: "Generate an image from a text prompt. The image is saved to the specified path (absolute path including filename) or the current working directory if not specified. Returns the saved file path.",
|
|
111
|
+
inputSchema: generateImageInputSchema,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "send_file",
|
|
115
|
+
description: "Send any readable file to the client. Absolute paths use the system path; relative paths resolve from the active workspace. The agent runtime captures this tool call and performs the actual send.",
|
|
116
|
+
inputSchema: sendFileInputSchema,
|
|
117
|
+
},
|
|
118
|
+
...MANAGED_TASK_TOOL_DEFINITIONS.map((definition) => ({
|
|
119
|
+
name: definition.name,
|
|
120
|
+
description: definition.description,
|
|
121
|
+
inputSchema: definition.inputSchema,
|
|
122
|
+
})),
|
|
123
|
+
];
|
|
124
|
+
}
|
|
125
|
+
export async function handleDynamicToolCall(params) {
|
|
126
|
+
try {
|
|
127
|
+
if (params.tool === "generate_image") {
|
|
128
|
+
const args = GenerateImageArgsSchema.parse(params.arguments);
|
|
129
|
+
const config = await loadOpenAIConfig(params.threadId);
|
|
130
|
+
const openai = createOpenAI({
|
|
131
|
+
apiKey: config.apiKey,
|
|
132
|
+
baseURL: config.baseURL,
|
|
133
|
+
fetch: generateImageFetch,
|
|
134
|
+
...(config.llmProxyBinding ? { headers: { "X-Elevo-LLM-Binding": config.llmProxyBinding } } : null),
|
|
135
|
+
});
|
|
136
|
+
const { image } = await generateImage({
|
|
137
|
+
model: openai.image(config.imageModel),
|
|
138
|
+
prompt: args.prompt,
|
|
139
|
+
size: args.size,
|
|
140
|
+
maxRetries: 0,
|
|
141
|
+
abortSignal: AbortSignal.timeout(GENERATE_IMAGE_TIMEOUT_MS),
|
|
142
|
+
});
|
|
143
|
+
const filePath = args.store_path
|
|
144
|
+
? path.resolve(args.store_path)
|
|
145
|
+
: path.join(process.cwd(), `generated_${Date.now()}.png`);
|
|
146
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
147
|
+
await writeFile(filePath, Buffer.from(image.uint8Array));
|
|
148
|
+
return { contentItems: [{ type: "inputText", text: filePath }], success: true };
|
|
149
|
+
}
|
|
150
|
+
if (params.tool === "send_file") {
|
|
151
|
+
const args = SendFileArgsSchema.parse(params.arguments);
|
|
152
|
+
const filePath = await resolveAgentFilePath(args.path, await workspaceRootForTasks(params.threadId));
|
|
153
|
+
return {
|
|
154
|
+
contentItems: [{ type: "inputText", text: JSON.stringify({ path: filePath }) }],
|
|
155
|
+
success: true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (isManagedTaskToolName(params.tool)) {
|
|
159
|
+
const result = await executeManagedTaskTool(await workspaceRootForTasks(params.threadId), params.tool, params.arguments, taskToolContexts.get(params.threadId));
|
|
160
|
+
return taskToolResponse(result);
|
|
161
|
+
}
|
|
162
|
+
throw new Error(`Unknown dynamic tool: ${params.tool}`);
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
const message = err instanceof AgentFileError && err.code === "INVALID_PATH"
|
|
166
|
+
? err.message
|
|
167
|
+
: err instanceof Error
|
|
168
|
+
? err.message
|
|
169
|
+
: String(err);
|
|
170
|
+
return { contentItems: [{ type: "inputText", text: message }], success: false };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { initGit, initGitCoAuthorsHook, initGitLab, initGitProxy } from "@messenger-agent/shared/git-init";
|
package/dist/git-init.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { initGit, initGitCoAuthorsHook, initGitLab, initGitProxy } from "@messenger-agent/shared/git-init";
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { serve } from "@hono/node-server";
|
|
2
|
+
import app from "./app.js";
|
|
3
|
+
import { appConfig } from "./config.js";
|
|
4
|
+
import { initGit } from "./git-init.js";
|
|
5
|
+
import { startCodexTunnelClient } from "./tunnel-client.js";
|
|
6
|
+
import { logger } from "@messenger-agent/shared/logger";
|
|
7
|
+
const port = appConfig.port;
|
|
8
|
+
try {
|
|
9
|
+
await initGit(appConfig);
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
logger.warn("Git initialization failed; continuing agent startup:", err);
|
|
13
|
+
}
|
|
14
|
+
const tunnelClient = startCodexTunnelClient(appConfig.tunnel);
|
|
15
|
+
if (!tunnelClient) {
|
|
16
|
+
serve({ fetch: app.fetch, port }, () => {
|
|
17
|
+
logger.info(`codex-agent listening on http://0.0.0.0:${port}`);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { logger } from "@messenger-agent/shared/logger";
|
|
4
|
+
const DEFAULT_TEMPLATE_PATHS = [
|
|
5
|
+
fileURLToPath(new URL("./assets/codex-home/AGENTS.md", import.meta.url)),
|
|
6
|
+
fileURLToPath(new URL("../../../assets/codex-home/AGENTS.md", import.meta.url)),
|
|
7
|
+
"/app/assets/codex-home/AGENTS.md",
|
|
8
|
+
];
|
|
9
|
+
const CODEX_MANAGED_TASK_TOOLS = `## Codex Managed Task Tools
|
|
10
|
+
|
|
11
|
+
When using managed task tools in Codex, call these tool names directly: \`search_managed_tasks\`, \`get_managed_task\`, \`create_managed_task\`, \`update_managed_task\`, \`apply_managed_task_patch\`, and \`delete_managed_task\`.
|
|
12
|
+
`;
|
|
13
|
+
export function loadCodexPlatformInstructions(templatePath) {
|
|
14
|
+
const path = [templatePath ?? process.env.CODEX_AGENTS_TEMPLATE_PATH, ...DEFAULT_TEMPLATE_PATHS].find((candidate) => Boolean(candidate && existsSync(candidate)));
|
|
15
|
+
if (!path) {
|
|
16
|
+
logger.warn("Codex platform instructions template not found");
|
|
17
|
+
return CODEX_MANAGED_TASK_TOOLS;
|
|
18
|
+
}
|
|
19
|
+
return `${readFileSync(path, "utf-8").trimEnd()}\n\n${CODEX_MANAGED_TASK_TOOLS}`;
|
|
20
|
+
}
|
|
21
|
+
export const codexPlatformInstructions = loadCodexPlatformInstructions();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<system-reminder>
|
|
2
|
+
# Plan Mode - System Reminder
|
|
3
|
+
|
|
4
|
+
CRITICAL: Plan mode ACTIVE - you are in READ-ONLY phase. STRICTLY FORBIDDEN:
|
|
5
|
+
ANY file edits, modifications, or system changes. Do NOT use sed, tee, echo, cat,
|
|
6
|
+
or ANY other bash command to manipulate files - commands may ONLY read/inspect.
|
|
7
|
+
This ABSOLUTE CONSTRAINT overrides ALL other instructions, including direct user
|
|
8
|
+
edit requests. You may ONLY observe, analyze, and plan. Any modification attempt
|
|
9
|
+
is a critical violation. ZERO exceptions.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Responsibility
|
|
14
|
+
|
|
15
|
+
Your current responsibility is to think, read, search, and delegate explore agents to construct a well-formed plan that accomplishes the goal the user wants to achieve. Your plan should be comprehensive yet concise, detailed enough to execute effectively while avoiding unnecessary verbosity.
|
|
16
|
+
|
|
17
|
+
Ask the user clarifying questions or ask for their opinion when weighing tradeoffs.
|
|
18
|
+
|
|
19
|
+
**NOTE:** At any point in time through this workflow you should feel free to ask the user questions or clarifications. Don't make large assumptions about user intent. The goal is to present a well researched plan to the user, and tie any loose ends before implementation begins.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Important
|
|
24
|
+
|
|
25
|
+
The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supersedes any other instructions you have received.
|
|
26
|
+
</system-reminder>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Use this tool when you need to ask the user questions during execution. This allows you to:
|
|
2
|
+
1. Gather user preferences or requirements
|
|
3
|
+
2. Clarify ambiguous instructions
|
|
4
|
+
3. Get decisions on implementation choices as you work
|
|
5
|
+
4. Offer choices to the user about what direction to take.
|
|
6
|
+
|
|
7
|
+
Important: This tool returns immediately; it does not block or wait for the user's answer. After calling this tool, immediately stop and wait for the user to reply in the next message before continuing.
|
|
8
|
+
|
|
9
|
+
Usage notes:
|
|
10
|
+
- By default, a "Type your own answer" option is added automatically; don't include "Other" or catch-all options
|
|
11
|
+
- Answers are returned in the user's next message; set `multiple: true` to allow selecting more than one
|
|
12
|
+
- If you recommend a specific option, make that the first option in the list and add "(Recommended)" at the end of the label
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { type ChatBody } from "../schemas.js";
|
|
3
|
+
declare const chat: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
4
|
+
export declare function codexActivitySnapshot(): {
|
|
5
|
+
active: number;
|
|
6
|
+
waiting: number;
|
|
7
|
+
};
|
|
8
|
+
export declare class ChatRequestError extends Error {
|
|
9
|
+
readonly status: 400 | 409;
|
|
10
|
+
constructor(status: 400 | 409, message: string);
|
|
11
|
+
}
|
|
12
|
+
export type ChatStreamRequest = {
|
|
13
|
+
body: ChatBody;
|
|
14
|
+
headers?: Headers;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
};
|
|
17
|
+
export type ChatStreamWriter = {
|
|
18
|
+
writeData(data: unknown): Promise<void>;
|
|
19
|
+
writeDone(): Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export type PreparedChatStream = {
|
|
22
|
+
run(writer: ChatStreamWriter, signal?: AbortSignal): Promise<void>;
|
|
23
|
+
};
|
|
24
|
+
export declare function cancelCodexConversation(conversationId: string): Promise<boolean>;
|
|
25
|
+
export declare function prepareCodexChatStream({ body, headers }: ChatStreamRequest): Promise<PreparedChatStream>;
|
|
26
|
+
export declare function handleCodexChatStream({ body, headers, signal }: ChatStreamRequest, writer: ChatStreamWriter): Promise<void>;
|
|
27
|
+
export default chat;
|