@helmisatria/mcp-chrome-bridge 1.0.30
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/README.md +183 -0
- package/dist/README.md +25 -0
- package/dist/agent/attachment-service.d.ts +83 -0
- package/dist/agent/attachment-service.js +370 -0
- package/dist/agent/attachment-service.js.map +1 -0
- package/dist/agent/ccr-detector.d.ts +59 -0
- package/dist/agent/ccr-detector.js +311 -0
- package/dist/agent/ccr-detector.js.map +1 -0
- package/dist/agent/chat-service.d.ts +50 -0
- package/dist/agent/chat-service.js +439 -0
- package/dist/agent/chat-service.js.map +1 -0
- package/dist/agent/db/client.d.ts +26 -0
- package/dist/agent/db/client.js +244 -0
- package/dist/agent/db/client.js.map +1 -0
- package/dist/agent/db/index.d.ts +5 -0
- package/dist/agent/db/index.js +22 -0
- package/dist/agent/db/index.js.map +1 -0
- package/dist/agent/db/schema.d.ts +711 -0
- package/dist/agent/db/schema.js +121 -0
- package/dist/agent/db/schema.js.map +1 -0
- package/dist/agent/directory-picker.d.ts +11 -0
- package/dist/agent/directory-picker.js +149 -0
- package/dist/agent/directory-picker.js.map +1 -0
- package/dist/agent/engines/claude.d.ts +79 -0
- package/dist/agent/engines/claude.js +1338 -0
- package/dist/agent/engines/claude.js.map +1 -0
- package/dist/agent/engines/codex.d.ts +48 -0
- package/dist/agent/engines/codex.js +822 -0
- package/dist/agent/engines/codex.js.map +1 -0
- package/dist/agent/engines/types.d.ts +133 -0
- package/dist/agent/engines/types.js +3 -0
- package/dist/agent/engines/types.js.map +1 -0
- package/dist/agent/message-service.d.ts +56 -0
- package/dist/agent/message-service.js +198 -0
- package/dist/agent/message-service.js.map +1 -0
- package/dist/agent/open-project.d.ts +25 -0
- package/dist/agent/open-project.js +469 -0
- package/dist/agent/open-project.js.map +1 -0
- package/dist/agent/project-service.d.ts +49 -0
- package/dist/agent/project-service.js +254 -0
- package/dist/agent/project-service.js.map +1 -0
- package/dist/agent/project-types.d.ts +27 -0
- package/dist/agent/project-types.js +3 -0
- package/dist/agent/project-types.js.map +1 -0
- package/dist/agent/session-service.d.ts +198 -0
- package/dist/agent/session-service.js +292 -0
- package/dist/agent/session-service.js.map +1 -0
- package/dist/agent/storage.d.ts +27 -0
- package/dist/agent/storage.js +73 -0
- package/dist/agent/storage.js.map +1 -0
- package/dist/agent/stream-manager.d.ts +42 -0
- package/dist/agent/stream-manager.js +243 -0
- package/dist/agent/stream-manager.js.map +1 -0
- package/dist/agent/tool-bridge.d.ts +44 -0
- package/dist/agent/tool-bridge.js +50 -0
- package/dist/agent/tool-bridge.js.map +1 -0
- package/dist/agent/types.d.ts +6 -0
- package/dist/agent/types.js +3 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +224 -0
- package/dist/cli.js.map +1 -0
- package/dist/constant/index.d.ts +60 -0
- package/dist/constant/index.js +80 -0
- package/dist/constant/index.js.map +1 -0
- package/dist/file-handler.d.ts +41 -0
- package/dist/file-handler.js +295 -0
- package/dist/file-handler.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/mcp-server-stdio.d.ts +72 -0
- package/dist/mcp/mcp-server-stdio.js +143 -0
- package/dist/mcp/mcp-server-stdio.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +36 -0
- package/dist/mcp/mcp-server.js +26 -0
- package/dist/mcp/mcp-server.js.map +1 -0
- package/dist/mcp/register-tools.d.ts +2 -0
- package/dist/mcp/register-tools.js +148 -0
- package/dist/mcp/register-tools.js.map +1 -0
- package/dist/mcp/stdio-config.json +3 -0
- package/dist/native-messaging-host.d.ts +42 -0
- package/dist/native-messaging-host.js +312 -0
- package/dist/native-messaging-host.js.map +1 -0
- package/dist/run_host.bat +194 -0
- package/dist/run_host.sh +264 -0
- package/dist/scripts/browser-config.d.ts +28 -0
- package/dist/scripts/browser-config.js +229 -0
- package/dist/scripts/browser-config.js.map +1 -0
- package/dist/scripts/build.d.ts +1 -0
- package/dist/scripts/build.js +126 -0
- package/dist/scripts/build.js.map +1 -0
- package/dist/scripts/constant.d.ts +4 -0
- package/dist/scripts/constant.js +8 -0
- package/dist/scripts/constant.js.map +1 -0
- package/dist/scripts/doctor.d.ts +70 -0
- package/dist/scripts/doctor.js +930 -0
- package/dist/scripts/doctor.js.map +1 -0
- package/dist/scripts/postinstall.d.ts +2 -0
- package/dist/scripts/postinstall.js +246 -0
- package/dist/scripts/postinstall.js.map +1 -0
- package/dist/scripts/register-dev.d.ts +1 -0
- package/dist/scripts/register-dev.js +5 -0
- package/dist/scripts/register-dev.js.map +1 -0
- package/dist/scripts/register.d.ts +2 -0
- package/dist/scripts/register.js +28 -0
- package/dist/scripts/register.js.map +1 -0
- package/dist/scripts/report.d.ts +96 -0
- package/dist/scripts/report.js +686 -0
- package/dist/scripts/report.js.map +1 -0
- package/dist/scripts/utils.d.ts +64 -0
- package/dist/scripts/utils.js +443 -0
- package/dist/scripts/utils.js.map +1 -0
- package/dist/server/index.d.ts +35 -0
- package/dist/server/index.js +312 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/routes/agent.d.ts +21 -0
- package/dist/server/routes/agent.js +971 -0
- package/dist/server/routes/agent.js.map +1 -0
- package/dist/server/routes/index.d.ts +4 -0
- package/dist/server/routes/index.js +9 -0
- package/dist/server/routes/index.js.map +1 -0
- package/dist/trace-analyzer.d.ts +14 -0
- package/dist/trace-analyzer.js +113 -0
- package/dist/trace-analyzer.js.map +1 -0
- package/dist/util/logger.d.ts +1 -0
- package/dist/util/logger.js +43 -0
- package/dist/util/logger.js.map +1 -0
- package/package.json +91 -0
|
@@ -0,0 +1,822 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CodexEngine = void 0;
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const node_readline_1 = __importDefault(require("node:readline"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_crypto_1 = require("node:crypto");
|
|
11
|
+
const chrome_mcp_shared_1 = require("chrome-mcp-shared");
|
|
12
|
+
const tool_bridge_1 = require("../tool-bridge");
|
|
13
|
+
const project_service_1 = require("../project-service");
|
|
14
|
+
const constant_1 = require("../../constant");
|
|
15
|
+
/**
|
|
16
|
+
* CodexEngine integrates the Codex CLI as an AgentEngine implementation.
|
|
17
|
+
*
|
|
18
|
+
* The implementation is intentionally self-contained and does not persist messages;
|
|
19
|
+
* it focuses on streaming Codex JSON events into RealtimeEvent envelopes that the
|
|
20
|
+
* sidepanel UI can consume.
|
|
21
|
+
*
|
|
22
|
+
* 中文说明:该引擎基于 other/cweb 中 Codex 适配器的事件协议,完整处理
|
|
23
|
+
* item.started/item.delta/item.completed/item.failed/error 等事件,并
|
|
24
|
+
* 通过 AgentStreamManager 将编码后的 RealtimeEvent 推送给 sidepanel,
|
|
25
|
+
* 确保数据链路「Sidepanel → Native Server → Codex CLI → Sidepanel」闭环。
|
|
26
|
+
*/
|
|
27
|
+
class CodexEngine {
|
|
28
|
+
constructor(toolBridge) {
|
|
29
|
+
this.name = 'codex';
|
|
30
|
+
this.supportsMcp = false;
|
|
31
|
+
this.toolBridge = toolBridge !== null && toolBridge !== void 0 ? toolBridge : new tool_bridge_1.AgentToolBridge();
|
|
32
|
+
}
|
|
33
|
+
async initializeAndRun(options, ctx) {
|
|
34
|
+
var _a;
|
|
35
|
+
const { sessionId, instruction, model, projectRoot, projectId, requestId, signal, attachments, resolvedImagePaths, codexConfig, } = options;
|
|
36
|
+
const repoPath = this.resolveRepoPath(projectRoot);
|
|
37
|
+
// Check if already aborted
|
|
38
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
39
|
+
throw new Error('CodexEngine: execution was cancelled');
|
|
40
|
+
}
|
|
41
|
+
const normalizedInstruction = instruction.trim();
|
|
42
|
+
if (!normalizedInstruction) {
|
|
43
|
+
throw new Error('CodexEngine: instruction must not be empty');
|
|
44
|
+
}
|
|
45
|
+
// Merge user config with defaults
|
|
46
|
+
const resolvedConfig = {
|
|
47
|
+
...chrome_mcp_shared_1.DEFAULT_CODEX_CONFIG,
|
|
48
|
+
...(codexConfig !== null && codexConfig !== void 0 ? codexConfig : {}),
|
|
49
|
+
};
|
|
50
|
+
// Ensure autoInstructions has a value
|
|
51
|
+
if (!((_a = resolvedConfig.autoInstructions) === null || _a === void 0 ? void 0 : _a.trim())) {
|
|
52
|
+
resolvedConfig.autoInstructions = chrome_mcp_shared_1.CODEX_AUTO_INSTRUCTIONS;
|
|
53
|
+
}
|
|
54
|
+
// Resolve project-scoped Chrome MCP toggle (default: enabled)
|
|
55
|
+
const enableChromeMcp = await (async () => {
|
|
56
|
+
if (!projectId)
|
|
57
|
+
return true;
|
|
58
|
+
try {
|
|
59
|
+
const project = await (0, project_service_1.getProject)(projectId);
|
|
60
|
+
return (project === null || project === void 0 ? void 0 : project.enableChromeMcp) !== false;
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
64
|
+
console.error(`[CodexEngine] Failed to load project enableChromeMcp, defaulting to enabled: ${message}`);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
// Optionally append project context to the prompt
|
|
69
|
+
const prompt = resolvedConfig.appendProjectContext
|
|
70
|
+
? await this.appendProjectContext(normalizedInstruction, repoPath)
|
|
71
|
+
: normalizedInstruction;
|
|
72
|
+
const executable = process.platform === 'win32' ? 'codex.cmd' : 'codex';
|
|
73
|
+
const args = [
|
|
74
|
+
'exec',
|
|
75
|
+
'--json',
|
|
76
|
+
'--skip-git-repo-check',
|
|
77
|
+
'--dangerously-bypass-approvals-and-sandbox',
|
|
78
|
+
'--color',
|
|
79
|
+
'never',
|
|
80
|
+
'--cd',
|
|
81
|
+
repoPath,
|
|
82
|
+
];
|
|
83
|
+
// Add Codex configuration arguments
|
|
84
|
+
args.push(...this.buildCodexConfigArgs(resolvedConfig));
|
|
85
|
+
// Inject local Chrome MCP server via runtime config override (no global codex config mutation)
|
|
86
|
+
// Use a unique server name to avoid collision with any existing global config
|
|
87
|
+
if (enableChromeMcp) {
|
|
88
|
+
const chromeMcpUrl = (0, constant_1.getChromeMcpUrl)();
|
|
89
|
+
// Set both url and type for complete HTTP MCP server configuration
|
|
90
|
+
args.push('-c', `mcp_servers.chrome_mcp_http.url=${JSON.stringify(chromeMcpUrl)}`);
|
|
91
|
+
args.push('-c', `mcp_servers.chrome_mcp_http.type="http"`);
|
|
92
|
+
console.error(`[CodexEngine] Chrome MCP server enabled: ${chromeMcpUrl}`);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.error('[CodexEngine] Chrome MCP server disabled');
|
|
96
|
+
}
|
|
97
|
+
if (model && model.trim()) {
|
|
98
|
+
args.push('--model', model.trim());
|
|
99
|
+
}
|
|
100
|
+
// Process image attachments - prefer resolvedImagePaths (persisted), fallback to temp files
|
|
101
|
+
const tempFiles = [];
|
|
102
|
+
const hasResolvedPaths = resolvedImagePaths && resolvedImagePaths.length > 0;
|
|
103
|
+
if (hasResolvedPaths) {
|
|
104
|
+
// Use pre-resolved persistent paths (preferred - no temp files needed)
|
|
105
|
+
console.error(`[CodexEngine] Using ${resolvedImagePaths.length} pre-resolved image path(s)`);
|
|
106
|
+
for (const imagePath of resolvedImagePaths) {
|
|
107
|
+
args.push('--image', imagePath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if (attachments && attachments.length > 0) {
|
|
111
|
+
// Fallback: write base64 to temp files (legacy behavior)
|
|
112
|
+
for (const attachment of attachments) {
|
|
113
|
+
if (attachment.type === 'image') {
|
|
114
|
+
try {
|
|
115
|
+
const tempFile = await this.writeAttachmentToTemp(attachment);
|
|
116
|
+
tempFiles.push(tempFile);
|
|
117
|
+
args.push('--image', tempFile);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.error('[CodexEngine] Failed to write attachment to temp file:', err);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
args.push(prompt);
|
|
126
|
+
// Use explicit Promise wrapping to ensure child process errors are properly rejected.
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
var _a, _b;
|
|
129
|
+
const child = (0, node_child_process_1.spawn)(executable, args, {
|
|
130
|
+
cwd: repoPath,
|
|
131
|
+
env: this.buildCodexEnv(),
|
|
132
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
133
|
+
});
|
|
134
|
+
// State management
|
|
135
|
+
const stderrBuffer = [];
|
|
136
|
+
let hasCompleted = false;
|
|
137
|
+
let timedOut = false;
|
|
138
|
+
let settled = false;
|
|
139
|
+
let timeoutHandle = null;
|
|
140
|
+
// Readline interface - declared early to avoid TDZ issues in finish()
|
|
141
|
+
let rl = null;
|
|
142
|
+
// Assistant message state
|
|
143
|
+
let assistantBuffer = '';
|
|
144
|
+
let assistantMessageId = null;
|
|
145
|
+
let assistantCreatedAt = null;
|
|
146
|
+
const streamedToolHashes = new Set();
|
|
147
|
+
const activeCommands = new Map();
|
|
148
|
+
const thinkingSegments = [];
|
|
149
|
+
/**
|
|
150
|
+
* Cleanup temporary files created for image attachments.
|
|
151
|
+
*/
|
|
152
|
+
const cleanupTempFiles = async () => {
|
|
153
|
+
if (tempFiles.length === 0)
|
|
154
|
+
return;
|
|
155
|
+
const fs = await import('node:fs/promises');
|
|
156
|
+
for (const filePath of tempFiles) {
|
|
157
|
+
try {
|
|
158
|
+
await fs.unlink(filePath);
|
|
159
|
+
console.error(`[CodexEngine] Cleaned up temp file: ${filePath}`);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
// Ignore errors during cleanup - file may already be deleted
|
|
163
|
+
console.error(`[CodexEngine] Failed to cleanup temp file ${filePath}:`, err);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* Cleanup and settle the promise (resolve or reject).
|
|
169
|
+
* Waits for temp file cleanup to complete before settling.
|
|
170
|
+
*/
|
|
171
|
+
const finish = async (error) => {
|
|
172
|
+
if (settled)
|
|
173
|
+
return;
|
|
174
|
+
settled = true;
|
|
175
|
+
// Clear timeout
|
|
176
|
+
if (timeoutHandle) {
|
|
177
|
+
clearTimeout(timeoutHandle);
|
|
178
|
+
timeoutHandle = null;
|
|
179
|
+
}
|
|
180
|
+
// Close readline interface
|
|
181
|
+
if (rl) {
|
|
182
|
+
try {
|
|
183
|
+
rl.close();
|
|
184
|
+
}
|
|
185
|
+
catch (_a) {
|
|
186
|
+
// Ignore close errors during cleanup
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Kill child process if still running
|
|
190
|
+
if (!child.killed) {
|
|
191
|
+
try {
|
|
192
|
+
child.kill();
|
|
193
|
+
}
|
|
194
|
+
catch (_b) {
|
|
195
|
+
// Ignore kill errors during cleanup
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Cleanup temp files after process is killed (wait for completion)
|
|
199
|
+
await cleanupTempFiles();
|
|
200
|
+
// Settle the promise
|
|
201
|
+
if (error) {
|
|
202
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
resolve();
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
// Handle child process error immediately after spawn (e.g., command not found)
|
|
209
|
+
child.on('error', (error) => {
|
|
210
|
+
const message = error instanceof Error
|
|
211
|
+
? error.message
|
|
212
|
+
: stderrBuffer.slice(-5).join('\n') || 'Codex CLI failed to start';
|
|
213
|
+
void finish(new Error(`CodexEngine: ${message}`));
|
|
214
|
+
});
|
|
215
|
+
// Listen for abort signal to cancel execution
|
|
216
|
+
const abortHandler = signal
|
|
217
|
+
? () => {
|
|
218
|
+
console.error('[CodexEngine] Execution cancelled via abort signal');
|
|
219
|
+
void finish(new Error('CodexEngine: execution was cancelled'));
|
|
220
|
+
}
|
|
221
|
+
: null;
|
|
222
|
+
if (signal && abortHandler) {
|
|
223
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
|
224
|
+
}
|
|
225
|
+
// Collect stderr with bounded buffer
|
|
226
|
+
(_a = child.stderr) === null || _a === void 0 ? void 0 : _a.on('data', (chunk) => {
|
|
227
|
+
const text = String(chunk).trim();
|
|
228
|
+
if (!text)
|
|
229
|
+
return;
|
|
230
|
+
stderrBuffer.push(text);
|
|
231
|
+
// Keep only the most recent lines to prevent memory growth
|
|
232
|
+
if (stderrBuffer.length > CodexEngine.MAX_STDERR_LINES) {
|
|
233
|
+
stderrBuffer.splice(0, stderrBuffer.length - CodexEngine.MAX_STDERR_LINES);
|
|
234
|
+
}
|
|
235
|
+
console.error('[CodexEngine][stderr]', text);
|
|
236
|
+
});
|
|
237
|
+
rl = node_readline_1.default.createInterface({ input: child.stdout });
|
|
238
|
+
/**
|
|
239
|
+
* Build the assistant message payload, combining thinking and agent content.
|
|
240
|
+
*/
|
|
241
|
+
const buildAssistantPayload = () => {
|
|
242
|
+
const trimmedAssistant = assistantBuffer.trim();
|
|
243
|
+
const thinkingContent = thinkingSegments
|
|
244
|
+
.map((segment) => segment.trim())
|
|
245
|
+
.filter((segment) => segment.length > 0)
|
|
246
|
+
.map((segment) => `<thinking>${segment}</thinking>`)
|
|
247
|
+
.join('\n\n');
|
|
248
|
+
const parts = [];
|
|
249
|
+
if (thinkingContent) {
|
|
250
|
+
parts.push(thinkingContent);
|
|
251
|
+
}
|
|
252
|
+
if (trimmedAssistant) {
|
|
253
|
+
parts.push(trimmedAssistant);
|
|
254
|
+
}
|
|
255
|
+
return parts.join('\n\n').trim();
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Reset assistant buffers after emitting a final message.
|
|
259
|
+
*/
|
|
260
|
+
const resetAssistantBuffers = () => {
|
|
261
|
+
assistantBuffer = '';
|
|
262
|
+
thinkingSegments.length = 0;
|
|
263
|
+
assistantMessageId = null;
|
|
264
|
+
assistantCreatedAt = null;
|
|
265
|
+
};
|
|
266
|
+
// Helper: emit assistant message
|
|
267
|
+
const emitAssistant = (isFinal) => {
|
|
268
|
+
const content = buildAssistantPayload();
|
|
269
|
+
if (!content)
|
|
270
|
+
return;
|
|
271
|
+
if (!assistantMessageId) {
|
|
272
|
+
assistantMessageId = (0, node_crypto_1.randomUUID)();
|
|
273
|
+
}
|
|
274
|
+
if (!assistantCreatedAt) {
|
|
275
|
+
assistantCreatedAt = new Date().toISOString();
|
|
276
|
+
}
|
|
277
|
+
const message = {
|
|
278
|
+
id: assistantMessageId,
|
|
279
|
+
sessionId,
|
|
280
|
+
role: 'assistant',
|
|
281
|
+
content,
|
|
282
|
+
messageType: 'chat',
|
|
283
|
+
cliSource: this.name,
|
|
284
|
+
requestId,
|
|
285
|
+
isStreaming: !isFinal,
|
|
286
|
+
isFinal,
|
|
287
|
+
createdAt: assistantCreatedAt,
|
|
288
|
+
};
|
|
289
|
+
ctx.emit({ type: 'message', data: message });
|
|
290
|
+
};
|
|
291
|
+
// Helper: emit tool message with deduplication
|
|
292
|
+
const dispatchToolMessage = (content, metadata, messageType, isStreaming) => {
|
|
293
|
+
const trimmed = content.trim();
|
|
294
|
+
if (!trimmed)
|
|
295
|
+
return;
|
|
296
|
+
const hash = this.encodeHash(`${messageType}:${trimmed}:${JSON.stringify(metadata)}:${sessionId}:${requestId || ''}`).slice(0, 16);
|
|
297
|
+
if (streamedToolHashes.has(hash))
|
|
298
|
+
return;
|
|
299
|
+
streamedToolHashes.add(hash);
|
|
300
|
+
const message = {
|
|
301
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
302
|
+
sessionId,
|
|
303
|
+
role: 'tool',
|
|
304
|
+
content: trimmed,
|
|
305
|
+
messageType,
|
|
306
|
+
cliSource: this.name,
|
|
307
|
+
requestId,
|
|
308
|
+
isStreaming,
|
|
309
|
+
isFinal: !isStreaming,
|
|
310
|
+
createdAt: new Date().toISOString(),
|
|
311
|
+
metadata: { cli_type: 'codex', ...metadata },
|
|
312
|
+
};
|
|
313
|
+
ctx.emit({ type: 'message', data: message });
|
|
314
|
+
};
|
|
315
|
+
// Event handlers for specific item types
|
|
316
|
+
const emitCommandStart = (item) => {
|
|
317
|
+
var _a, _b;
|
|
318
|
+
const id = (_a = this.pickFirstString(item.id)) !== null && _a !== void 0 ? _a : (0, node_crypto_1.randomUUID)();
|
|
319
|
+
const command = this.pickFirstString(item.command);
|
|
320
|
+
activeCommands.set(id, { command });
|
|
321
|
+
dispatchToolMessage(command ? `Running: ${command}` : 'Running command', {
|
|
322
|
+
toolName: 'Bash',
|
|
323
|
+
tool_name: 'Bash',
|
|
324
|
+
command,
|
|
325
|
+
status: (_b = this.pickFirstString(item.status)) !== null && _b !== void 0 ? _b : 'in_progress',
|
|
326
|
+
}, 'tool_use', true);
|
|
327
|
+
};
|
|
328
|
+
const emitCommandResult = (item) => {
|
|
329
|
+
var _a, _b;
|
|
330
|
+
const id = this.pickFirstString(item.id);
|
|
331
|
+
const tracked = id ? activeCommands.get(id) : undefined;
|
|
332
|
+
if (id) {
|
|
333
|
+
activeCommands.delete(id);
|
|
334
|
+
}
|
|
335
|
+
const command = (_a = this.pickFirstString(item.command)) !== null && _a !== void 0 ? _a : tracked === null || tracked === void 0 ? void 0 : tracked.command;
|
|
336
|
+
const output = (_b = this.pickFirstString(item.aggregated_output)) !== null && _b !== void 0 ? _b : '';
|
|
337
|
+
const exitCode = typeof item.exit_code === 'number' ? item.exit_code : undefined;
|
|
338
|
+
const status = this.pickFirstString(item.status);
|
|
339
|
+
const isError = status === 'failed' || (typeof exitCode === 'number' && exitCode !== 0);
|
|
340
|
+
const summary = command ? `Ran: ${command}` : 'Executed shell command';
|
|
341
|
+
const exitSuffix = typeof exitCode === 'number' ? ` (exit ${exitCode})` : '';
|
|
342
|
+
const body = output.trim();
|
|
343
|
+
const fullContent = body ? `${summary}${exitSuffix}\n\n${body}` : `${summary}${exitSuffix}`;
|
|
344
|
+
dispatchToolMessage(fullContent, {
|
|
345
|
+
toolName: 'Bash',
|
|
346
|
+
tool_name: 'Bash',
|
|
347
|
+
command,
|
|
348
|
+
exitCode,
|
|
349
|
+
status,
|
|
350
|
+
output,
|
|
351
|
+
is_error: isError || undefined,
|
|
352
|
+
}, 'tool_result', false);
|
|
353
|
+
};
|
|
354
|
+
const emitFileChange = (item) => {
|
|
355
|
+
var _a;
|
|
356
|
+
const { content, metadata } = this.summarizeApplyPatch({
|
|
357
|
+
changes: item.changes,
|
|
358
|
+
});
|
|
359
|
+
const status = (_a = this.pickFirstString(item.status)) !== null && _a !== void 0 ? _a : 'completed';
|
|
360
|
+
const isError = status === 'failed';
|
|
361
|
+
const toolName = (metadata === null || metadata === void 0 ? void 0 : metadata.toolName) || (metadata === null || metadata === void 0 ? void 0 : metadata.tool_name) || 'Edit';
|
|
362
|
+
dispatchToolMessage(isError ? `Failed: ${content}` : content, { ...metadata, toolName, tool_name: toolName, status, is_error: isError || undefined }, 'tool_result', false);
|
|
363
|
+
};
|
|
364
|
+
const emitTodoListUpdate = (record, phase) => {
|
|
365
|
+
var _a;
|
|
366
|
+
const rawItems = this.extractTodoListItems(record);
|
|
367
|
+
const items = this.normalizeTodoListItems(rawItems);
|
|
368
|
+
const content = this.buildTodoListContent(items, phase);
|
|
369
|
+
const status = (_a = this.pickFirstString(record.status)) !== null && _a !== void 0 ? _a : (phase === 'completed' ? 'completed' : 'in_progress');
|
|
370
|
+
const metadata = this.createTodoListMetadata(items, phase, {
|
|
371
|
+
status,
|
|
372
|
+
planId: this.pickFirstString(record.id),
|
|
373
|
+
});
|
|
374
|
+
dispatchToolMessage(content, metadata, phase === 'completed' ? 'tool_result' : 'tool_use', phase === 'update');
|
|
375
|
+
};
|
|
376
|
+
// Item event handlers
|
|
377
|
+
const handleItemStarted = (item) => {
|
|
378
|
+
if (!item || typeof item !== 'object')
|
|
379
|
+
return;
|
|
380
|
+
const record = item;
|
|
381
|
+
const type = this.pickFirstString(record.type);
|
|
382
|
+
if (type === 'command_execution') {
|
|
383
|
+
emitCommandStart(record);
|
|
384
|
+
}
|
|
385
|
+
else if (type === 'todo_list') {
|
|
386
|
+
emitTodoListUpdate(record, 'started');
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
const handleItemDelta = (delta) => {
|
|
390
|
+
if (!delta || typeof delta !== 'object')
|
|
391
|
+
return;
|
|
392
|
+
const record = delta;
|
|
393
|
+
const type = this.pickFirstString(record.type);
|
|
394
|
+
if (type === 'agent_message') {
|
|
395
|
+
const text = this.pickFirstString(record.text);
|
|
396
|
+
if (text) {
|
|
397
|
+
assistantBuffer += text;
|
|
398
|
+
emitAssistant(false);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else if (type === 'reasoning') {
|
|
402
|
+
const text = this.pickFirstString(record.text);
|
|
403
|
+
if (text) {
|
|
404
|
+
thinkingSegments.push(text);
|
|
405
|
+
emitAssistant(false);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
else if (type === 'todo_list') {
|
|
409
|
+
emitTodoListUpdate(record, 'update');
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
const handleItemCompleted = (item) => {
|
|
413
|
+
if (!item || typeof item !== 'object')
|
|
414
|
+
return;
|
|
415
|
+
const record = item;
|
|
416
|
+
const type = this.pickFirstString(record.type);
|
|
417
|
+
switch (type) {
|
|
418
|
+
case 'command_execution':
|
|
419
|
+
emitCommandResult(record);
|
|
420
|
+
break;
|
|
421
|
+
case 'file_change':
|
|
422
|
+
emitFileChange(record);
|
|
423
|
+
break;
|
|
424
|
+
case 'todo_list':
|
|
425
|
+
emitTodoListUpdate(record, 'completed');
|
|
426
|
+
break;
|
|
427
|
+
case 'agent_message': {
|
|
428
|
+
const text = this.pickFirstString(record.text);
|
|
429
|
+
if (text)
|
|
430
|
+
assistantBuffer = text;
|
|
431
|
+
emitAssistant(true);
|
|
432
|
+
resetAssistantBuffers();
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
case 'reasoning': {
|
|
436
|
+
const text = this.pickFirstString(record.text);
|
|
437
|
+
if (text) {
|
|
438
|
+
thinkingSegments.push(text);
|
|
439
|
+
emitAssistant(false);
|
|
440
|
+
}
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
default: {
|
|
444
|
+
const text = this.pickFirstString(record.text);
|
|
445
|
+
if (text) {
|
|
446
|
+
thinkingSegments.push(text);
|
|
447
|
+
emitAssistant(false);
|
|
448
|
+
}
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
// Setup timeout
|
|
454
|
+
const timeoutMs = Number.parseInt(process.env.CODEX_ENGINE_TIMEOUT_MS || '', 10) || 15 * 60 * 1000;
|
|
455
|
+
timeoutHandle = setTimeout(() => {
|
|
456
|
+
timedOut = true;
|
|
457
|
+
// Close readline to exit the loop
|
|
458
|
+
try {
|
|
459
|
+
rl.close();
|
|
460
|
+
}
|
|
461
|
+
catch (_a) {
|
|
462
|
+
// Ignore
|
|
463
|
+
}
|
|
464
|
+
if (!child.killed) {
|
|
465
|
+
try {
|
|
466
|
+
child.kill();
|
|
467
|
+
}
|
|
468
|
+
catch (_b) {
|
|
469
|
+
// Ignore
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}, timeoutMs);
|
|
473
|
+
(_b = timeoutHandle.unref) === null || _b === void 0 ? void 0 : _b.call(timeoutHandle);
|
|
474
|
+
// Cleanup timeout and handle abnormal exit
|
|
475
|
+
child.on('close', (code, closeSignal) => {
|
|
476
|
+
if (timeoutHandle) {
|
|
477
|
+
clearTimeout(timeoutHandle);
|
|
478
|
+
timeoutHandle = null;
|
|
479
|
+
}
|
|
480
|
+
// If already timed out, settled, or completed normally, do nothing
|
|
481
|
+
if (timedOut || settled || hasCompleted) {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
// Build error detail from exit code and signal
|
|
485
|
+
const detailParts = [];
|
|
486
|
+
if (typeof code === 'number') {
|
|
487
|
+
detailParts.push(`exit code ${code}`);
|
|
488
|
+
}
|
|
489
|
+
if (closeSignal) {
|
|
490
|
+
detailParts.push(`signal ${closeSignal}`);
|
|
491
|
+
}
|
|
492
|
+
const detail = detailParts.length > 0 ? detailParts.join(', ') : 'unexpected shutdown';
|
|
493
|
+
// Emit final assistant message and mark as failed
|
|
494
|
+
emitAssistant(true);
|
|
495
|
+
resetAssistantBuffers();
|
|
496
|
+
hasCompleted = true;
|
|
497
|
+
void finish(new Error(`CodexEngine: process terminated (${detail})`));
|
|
498
|
+
});
|
|
499
|
+
// Main event processing loop (wrapped in IIFE to handle async properly)
|
|
500
|
+
void (async () => {
|
|
501
|
+
var _a, _b, _c, _d;
|
|
502
|
+
try {
|
|
503
|
+
for await (const line of rl) {
|
|
504
|
+
const trimmed = line.trim();
|
|
505
|
+
if (!trimmed)
|
|
506
|
+
continue;
|
|
507
|
+
let event;
|
|
508
|
+
try {
|
|
509
|
+
event = JSON.parse(trimmed);
|
|
510
|
+
}
|
|
511
|
+
catch (_e) {
|
|
512
|
+
console.warn('[CodexEngine] Failed to parse Codex event line:', trimmed);
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
const eventType = this.pickFirstString(event.type);
|
|
516
|
+
switch (eventType) {
|
|
517
|
+
case 'item.started':
|
|
518
|
+
handleItemStarted((_a = event.item) !== null && _a !== void 0 ? _a : null);
|
|
519
|
+
break;
|
|
520
|
+
case 'item.delta':
|
|
521
|
+
handleItemDelta((_b = event.delta) !== null && _b !== void 0 ? _b : null);
|
|
522
|
+
break;
|
|
523
|
+
case 'item.completed':
|
|
524
|
+
handleItemCompleted((_c = event.item) !== null && _c !== void 0 ? _c : null);
|
|
525
|
+
break;
|
|
526
|
+
case 'item.failed': {
|
|
527
|
+
const item = (_d = event.item) !== null && _d !== void 0 ? _d : null;
|
|
528
|
+
handleItemCompleted(item);
|
|
529
|
+
// Flush assistant message before throwing (aligned with other/cweb)
|
|
530
|
+
emitAssistant(true);
|
|
531
|
+
resetAssistantBuffers();
|
|
532
|
+
const msg = (item &&
|
|
533
|
+
typeof item === 'object' &&
|
|
534
|
+
this.pickFirstString(item.error)) ||
|
|
535
|
+
'Codex execution failed';
|
|
536
|
+
hasCompleted = true;
|
|
537
|
+
throw new Error(msg);
|
|
538
|
+
}
|
|
539
|
+
case 'error': {
|
|
540
|
+
// Flush assistant message before throwing (aligned with other/cweb)
|
|
541
|
+
emitAssistant(true);
|
|
542
|
+
resetAssistantBuffers();
|
|
543
|
+
const msg = this.pickFirstString(event.error) ||
|
|
544
|
+
this.pickFirstString(event.message) ||
|
|
545
|
+
stderrBuffer.slice(-5).join('\n') ||
|
|
546
|
+
'Codex execution error';
|
|
547
|
+
hasCompleted = true;
|
|
548
|
+
throw new Error(msg);
|
|
549
|
+
}
|
|
550
|
+
case 'turn.completed':
|
|
551
|
+
emitAssistant(true);
|
|
552
|
+
resetAssistantBuffers();
|
|
553
|
+
hasCompleted = true;
|
|
554
|
+
break;
|
|
555
|
+
default:
|
|
556
|
+
// Non-critical events are ignored
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
// Check for timeout after loop exits
|
|
561
|
+
if (timedOut) {
|
|
562
|
+
throw new Error('CodexEngine: execution timed out');
|
|
563
|
+
}
|
|
564
|
+
// Emit final assistant message if not already completed
|
|
565
|
+
if (!hasCompleted) {
|
|
566
|
+
emitAssistant(true);
|
|
567
|
+
resetAssistantBuffers();
|
|
568
|
+
hasCompleted = true;
|
|
569
|
+
}
|
|
570
|
+
await finish();
|
|
571
|
+
}
|
|
572
|
+
catch (error) {
|
|
573
|
+
await finish(error);
|
|
574
|
+
}
|
|
575
|
+
})();
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
resolveRepoPath(projectRoot) {
|
|
579
|
+
const base = (projectRoot && projectRoot.trim()) || process.env.MCP_AGENT_PROJECT_ROOT || process.cwd();
|
|
580
|
+
return node_path_1.default.resolve(base);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Append project context (file listing) to the prompt.
|
|
584
|
+
* Aligned with other/cweb implementation.
|
|
585
|
+
*/
|
|
586
|
+
async appendProjectContext(baseInstruction, repoPath) {
|
|
587
|
+
try {
|
|
588
|
+
const fs = await import('node:fs/promises');
|
|
589
|
+
const entries = await fs.readdir(repoPath, { withFileTypes: true });
|
|
590
|
+
const visible = entries
|
|
591
|
+
.filter((entry) => !entry.name.startsWith('.git') && entry.name !== 'AGENTS.md')
|
|
592
|
+
.map((entry) => entry.name);
|
|
593
|
+
if (visible.length === 0) {
|
|
594
|
+
return `${baseInstruction}
|
|
595
|
+
|
|
596
|
+
<current_project_context>
|
|
597
|
+
This is an empty project directory. Work directly in the current folder without creating extra subdirectories.
|
|
598
|
+
</current_project_context>`;
|
|
599
|
+
}
|
|
600
|
+
return `${baseInstruction}
|
|
601
|
+
|
|
602
|
+
<current_project_context>
|
|
603
|
+
Current files in project directory: ${visible.sort().join(', ')}
|
|
604
|
+
Work directly in the current directory. Do not create subdirectories unless specifically requested.
|
|
605
|
+
</current_project_context>`;
|
|
606
|
+
}
|
|
607
|
+
catch (error) {
|
|
608
|
+
console.warn('[CodexEngine] Failed to append project context:', error);
|
|
609
|
+
return baseInstruction;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Build Codex CLI configuration arguments from the resolved config.
|
|
614
|
+
* Aligned with other/cweb implementation for feature parity.
|
|
615
|
+
*/
|
|
616
|
+
buildCodexConfigArgs(config) {
|
|
617
|
+
const args = [];
|
|
618
|
+
const pushConfig = (key, value) => {
|
|
619
|
+
args.push('-c', `${key}=${String(value)}`);
|
|
620
|
+
};
|
|
621
|
+
pushConfig('include_apply_patch_tool', config.includeApplyPatchTool);
|
|
622
|
+
pushConfig('include_plan_tool', config.includePlanTool);
|
|
623
|
+
pushConfig('tools.web_search_request', config.enableWebSearch);
|
|
624
|
+
pushConfig('use_experimental_streamable_shell_tool', config.useStreamableShell);
|
|
625
|
+
pushConfig('sandbox_mode', config.sandboxMode);
|
|
626
|
+
pushConfig('max_turns', config.maxTurns);
|
|
627
|
+
pushConfig('max_thinking_tokens', config.maxThinkingTokens);
|
|
628
|
+
pushConfig('reasoning_effort', config.reasoningEffort);
|
|
629
|
+
args.push('-c', `instructions=${JSON.stringify(config.autoInstructions)}`);
|
|
630
|
+
return args;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Write an attachment to a temporary file and return its path.
|
|
634
|
+
*/
|
|
635
|
+
async writeAttachmentToTemp(attachment) {
|
|
636
|
+
const os = await import('node:os');
|
|
637
|
+
const fs = await import('node:fs/promises');
|
|
638
|
+
const tempDir = os.tmpdir();
|
|
639
|
+
const ext = attachment.mimeType.split('/')[1] || 'bin';
|
|
640
|
+
const sanitizedName = attachment.name.replace(/[^a-zA-Z0-9.-]/g, '_');
|
|
641
|
+
const fileName = `mcp-agent-${Date.now()}-${sanitizedName}.${ext}`;
|
|
642
|
+
const filePath = node_path_1.default.join(tempDir, fileName);
|
|
643
|
+
const buffer = Buffer.from(attachment.dataBase64, 'base64');
|
|
644
|
+
await fs.writeFile(filePath, buffer);
|
|
645
|
+
return filePath;
|
|
646
|
+
}
|
|
647
|
+
buildCodexEnv() {
|
|
648
|
+
const env = { ...process.env };
|
|
649
|
+
const extraPaths = [];
|
|
650
|
+
const globalPath = process.env.NPM_GLOBAL_PATH;
|
|
651
|
+
if (globalPath) {
|
|
652
|
+
extraPaths.push(globalPath);
|
|
653
|
+
}
|
|
654
|
+
// Enhanced Windows PATH handling (aligned with other/cweb)
|
|
655
|
+
if (process.platform === 'win32') {
|
|
656
|
+
const appData = process.env.APPDATA;
|
|
657
|
+
const localApp = process.env.LOCALAPPDATA;
|
|
658
|
+
if (appData) {
|
|
659
|
+
extraPaths.push(node_path_1.default.join(appData, 'npm'));
|
|
660
|
+
}
|
|
661
|
+
if (localApp) {
|
|
662
|
+
extraPaths.push(node_path_1.default.join(localApp, 'Programs', 'nodejs'));
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (extraPaths.length > 0) {
|
|
666
|
+
const currentPath = env.PATH || env.Path || '';
|
|
667
|
+
env.PATH = [...extraPaths, currentPath].filter(Boolean).join(node_path_1.default.delimiter);
|
|
668
|
+
}
|
|
669
|
+
return env;
|
|
670
|
+
}
|
|
671
|
+
pickFirstString(value) {
|
|
672
|
+
if (typeof value === 'string') {
|
|
673
|
+
const trimmed = value.trim();
|
|
674
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
675
|
+
}
|
|
676
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
677
|
+
return String(value);
|
|
678
|
+
}
|
|
679
|
+
if (Array.isArray(value)) {
|
|
680
|
+
for (const entry of value) {
|
|
681
|
+
const candidate = this.pickFirstString(entry);
|
|
682
|
+
if (candidate) {
|
|
683
|
+
return candidate;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return undefined;
|
|
687
|
+
}
|
|
688
|
+
if (value && typeof value === 'object') {
|
|
689
|
+
const record = value;
|
|
690
|
+
for (const key of Object.keys(record)) {
|
|
691
|
+
const candidate = this.pickFirstString(record[key]);
|
|
692
|
+
if (candidate) {
|
|
693
|
+
return candidate;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return undefined;
|
|
698
|
+
}
|
|
699
|
+
summarizeApplyPatch(payload) {
|
|
700
|
+
const changes = payload === null || payload === void 0 ? void 0 : payload.changes;
|
|
701
|
+
const files = [];
|
|
702
|
+
if (Array.isArray(changes)) {
|
|
703
|
+
for (const entry of changes) {
|
|
704
|
+
const file = entry && typeof entry === 'object'
|
|
705
|
+
? entry.path ||
|
|
706
|
+
entry.file
|
|
707
|
+
: undefined;
|
|
708
|
+
if (file && typeof file === 'string') {
|
|
709
|
+
files.push(file);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
else if (changes && typeof changes === 'object') {
|
|
714
|
+
for (const key of Object.keys(changes)) {
|
|
715
|
+
files.push(key);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
const unique = Array.from(new Set(files));
|
|
719
|
+
const summary = unique.length === 0
|
|
720
|
+
? 'Applied file changes'
|
|
721
|
+
: unique.length === 1
|
|
722
|
+
? `Updated ${unique[0]}`
|
|
723
|
+
: `Updated ${unique.length} files (${unique
|
|
724
|
+
.slice(0, 3)
|
|
725
|
+
.join(', ')}${unique.length > 3 ? ', ...' : ''})`;
|
|
726
|
+
return {
|
|
727
|
+
content: summary,
|
|
728
|
+
metadata: {
|
|
729
|
+
files: unique,
|
|
730
|
+
},
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
extractTodoListItems(record) {
|
|
734
|
+
if (Array.isArray(record.items)) {
|
|
735
|
+
return record.items;
|
|
736
|
+
}
|
|
737
|
+
const nestedItem = record.item;
|
|
738
|
+
if (nestedItem &&
|
|
739
|
+
typeof nestedItem === 'object' &&
|
|
740
|
+
Array.isArray(nestedItem.items)) {
|
|
741
|
+
return nestedItem.items;
|
|
742
|
+
}
|
|
743
|
+
const delta = record.delta;
|
|
744
|
+
if (delta &&
|
|
745
|
+
typeof delta === 'object' &&
|
|
746
|
+
Array.isArray(delta.items)) {
|
|
747
|
+
return delta.items;
|
|
748
|
+
}
|
|
749
|
+
return [];
|
|
750
|
+
}
|
|
751
|
+
normalizeTodoListItems(input) {
|
|
752
|
+
if (!Array.isArray(input)) {
|
|
753
|
+
return [];
|
|
754
|
+
}
|
|
755
|
+
const result = [];
|
|
756
|
+
input.forEach((entry, index) => {
|
|
757
|
+
var _a;
|
|
758
|
+
if (!entry || typeof entry !== 'object') {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const record = entry;
|
|
762
|
+
const text = (_a = this.pickFirstString(record.text)) !== null && _a !== void 0 ? _a : `Step ${index + 1}`;
|
|
763
|
+
const completed = record.completed === true || record.done === true;
|
|
764
|
+
result.push({
|
|
765
|
+
text,
|
|
766
|
+
completed,
|
|
767
|
+
index,
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
return result;
|
|
771
|
+
}
|
|
772
|
+
buildTodoListContent(items, phase) {
|
|
773
|
+
if (items.length === 0) {
|
|
774
|
+
switch (phase) {
|
|
775
|
+
case 'started':
|
|
776
|
+
return 'Started plan with no explicit steps.';
|
|
777
|
+
case 'completed':
|
|
778
|
+
return 'Plan completed.';
|
|
779
|
+
default:
|
|
780
|
+
return 'Plan updated.';
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
const header = phase === 'completed'
|
|
784
|
+
? 'Plan completed:'
|
|
785
|
+
: phase === 'started'
|
|
786
|
+
? 'Plan generated:'
|
|
787
|
+
: 'Plan updated:';
|
|
788
|
+
const stepLines = items.map((item, idx) => {
|
|
789
|
+
const bullet = item.completed ? '✅' : '⬜️';
|
|
790
|
+
const label = `Step ${idx + 1}`;
|
|
791
|
+
return `${bullet} ${label}: ${item.text}`;
|
|
792
|
+
});
|
|
793
|
+
return [header, ...stepLines].join('\n');
|
|
794
|
+
}
|
|
795
|
+
createTodoListMetadata(items, phase, extra) {
|
|
796
|
+
const totalSteps = items.length;
|
|
797
|
+
const completedSteps = items.filter((item) => item.completed).length;
|
|
798
|
+
return {
|
|
799
|
+
toolName: 'Plan',
|
|
800
|
+
tool_name: 'Plan',
|
|
801
|
+
planPhase: phase,
|
|
802
|
+
planStatus: phase === 'completed' ? 'completed' : 'in_progress',
|
|
803
|
+
totalSteps,
|
|
804
|
+
completedSteps,
|
|
805
|
+
items: items.map(({ text, completed, index }) => ({
|
|
806
|
+
text,
|
|
807
|
+
completed,
|
|
808
|
+
index,
|
|
809
|
+
})),
|
|
810
|
+
...(extra !== null && extra !== void 0 ? extra : {}),
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
encodeHash(value) {
|
|
814
|
+
return Buffer.from(value, 'utf-8').toString('base64');
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
exports.CodexEngine = CodexEngine;
|
|
818
|
+
/**
|
|
819
|
+
* Maximum number of stderr lines to keep in memory to avoid unbounded growth.
|
|
820
|
+
*/
|
|
821
|
+
CodexEngine.MAX_STDERR_LINES = 200;
|
|
822
|
+
//# sourceMappingURL=codex.js.map
|