@yemi33/minions 0.1.2066 → 0.1.2068
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 +3 -3
- package/docs/runtime-adapters.md +20 -18
- package/engine/db/index.js +55 -8
- package/engine/db/migrations/002-dispatches.js +113 -0
- package/engine/db/migrations/003-work-items.js +128 -0
- package/engine/dispatch-store.js +316 -0
- package/engine/dispatch.js +50 -3
- package/engine/llm.js +4 -3
- package/engine/queries.js +47 -19
- package/engine/restart-health.js +8 -2
- package/engine/runtimes/codex.js +862 -0
- package/engine/runtimes/index.js +1 -0
- package/engine/shared.js +37 -8
- package/engine/work-items-store.js +351 -0
- package/package.json +1 -1
|
@@ -0,0 +1,862 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* engine/runtimes/codex.js - OpenAI Codex CLI runtime adapter.
|
|
3
|
+
*
|
|
4
|
+
* Implements the runtime contract documented in engine/runtimes/claude.js.
|
|
5
|
+
* Codex-specific behavior stays here: binary resolution, `codex exec` argv,
|
|
6
|
+
* prompt construction, JSONL parsing, session persistence, skill roots, and
|
|
7
|
+
* error normalization.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const os = require('os');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { execFileSync, execSync } = require('child_process');
|
|
14
|
+
const { FAILURE_CLASS, safeWrite, ts, resolveEngineCacheDir } = require('../shared');
|
|
15
|
+
|
|
16
|
+
const ENGINE_DIR = __dirname.replace(/[\\/]runtimes$/, '');
|
|
17
|
+
const MINIONS_DIR = path.resolve(ENGINE_DIR, '..');
|
|
18
|
+
const _CACHE_DIR = resolveEngineCacheDir(ENGINE_DIR);
|
|
19
|
+
const isWin = process.platform === 'win32';
|
|
20
|
+
const RUNTIME_NAME = 'codex';
|
|
21
|
+
|
|
22
|
+
const CAPS_FILE = path.join(_CACHE_DIR, 'codex-caps.json');
|
|
23
|
+
const MODELS_CACHE = path.join(_CACHE_DIR, 'codex-models.json');
|
|
24
|
+
const CAPS_SCHEMA_VERSION = 2;
|
|
25
|
+
|
|
26
|
+
const PLATFORM_PACKAGE_BY_TRIPLE = Object.freeze({
|
|
27
|
+
'x86_64-unknown-linux-musl': '@openai/codex-linux-x64',
|
|
28
|
+
'aarch64-unknown-linux-musl': '@openai/codex-linux-arm64',
|
|
29
|
+
'x86_64-apple-darwin': '@openai/codex-darwin-x64',
|
|
30
|
+
'aarch64-apple-darwin': '@openai/codex-darwin-arm64',
|
|
31
|
+
'x86_64-pc-windows-msvc': '@openai/codex-win32-x64',
|
|
32
|
+
'aarch64-pc-windows-msvc': '@openai/codex-win32-arm64',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
function _safeJson(p) {
|
|
36
|
+
try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function _safeWriteJson(p, obj) {
|
|
40
|
+
try { fs.writeFileSync(p, JSON.stringify(obj, null, 2)); } catch { /* best effort */ }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function _execSyncCapture(cmd, env, timeoutMs = 10000) {
|
|
44
|
+
return execSync(cmd, { encoding: 'utf8', env, timeout: timeoutMs, windowsHide: true });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function _execFileCapture(bin, args, { env = process.env, timeoutMs = 10000, native = true } = {}) {
|
|
48
|
+
const execBin = native ? bin : process.execPath;
|
|
49
|
+
const execArgs = native ? args : [bin, ...args];
|
|
50
|
+
return execFileSync(execBin, execArgs, { encoding: 'utf8', env, timeout: timeoutMs, windowsHide: true });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function _codexBinaryName(platform = process.platform) {
|
|
54
|
+
return platform === 'win32' ? 'codex.exe' : 'codex';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _nativeVendorHit(vendorRoot, triple = _platformTriple(process.platform, process.arch)) {
|
|
58
|
+
if (!vendorRoot || !triple) return null;
|
|
59
|
+
const binName = _codexBinaryName();
|
|
60
|
+
const packageBin = path.join(vendorRoot, triple, 'bin', binName);
|
|
61
|
+
if (fs.existsSync(packageBin)) return { bin: packageBin, source: 'npm-platform-vendor' };
|
|
62
|
+
const legacyBin = path.join(vendorRoot, triple, 'codex', binName);
|
|
63
|
+
if (fs.existsSync(legacyBin)) return { bin: legacyBin, source: 'npm-vendor' };
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function _resolvePlatformPackageDir(pkgDir, triple = _platformTriple(process.platform, process.arch)) {
|
|
68
|
+
const platformPackage = PLATFORM_PACKAGE_BY_TRIPLE[triple];
|
|
69
|
+
if (!platformPackage || !pkgDir) return null;
|
|
70
|
+
|
|
71
|
+
const candidates = [];
|
|
72
|
+
try {
|
|
73
|
+
const packageJson = require.resolve(`${platformPackage}/package.json`, { paths: [pkgDir, path.dirname(pkgDir), MINIONS_DIR] });
|
|
74
|
+
candidates.push(path.dirname(packageJson));
|
|
75
|
+
} catch {}
|
|
76
|
+
|
|
77
|
+
const scopedName = platformPackage.replace(/^@openai\//, '');
|
|
78
|
+
candidates.push(
|
|
79
|
+
path.join(path.dirname(pkgDir), scopedName),
|
|
80
|
+
path.join(path.dirname(path.dirname(pkgDir)), '@openai', scopedName),
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
for (const candidate of candidates) {
|
|
84
|
+
try {
|
|
85
|
+
if (candidate && fs.existsSync(path.join(candidate, 'package.json'))) return candidate;
|
|
86
|
+
} catch {}
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function _resolveNativeCodexBinaryForPackage(pkgDir) {
|
|
92
|
+
if (!pkgDir) return null;
|
|
93
|
+
const localVendor = _nativeVendorHit(path.join(pkgDir, 'vendor'));
|
|
94
|
+
if (localVendor) return localVendor;
|
|
95
|
+
|
|
96
|
+
const platformPkg = _resolvePlatformPackageDir(pkgDir);
|
|
97
|
+
if (!platformPkg) return null;
|
|
98
|
+
return _nativeVendorHit(path.join(platformPkg, 'vendor'));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function _looksLikeCodexPackageDir(p) {
|
|
102
|
+
if (!p) return false;
|
|
103
|
+
try {
|
|
104
|
+
return fs.statSync(p).isDirectory()
|
|
105
|
+
&& fs.existsSync(path.join(p, 'package.json'))
|
|
106
|
+
&& fs.existsSync(path.join(p, 'bin', 'codex.js'));
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function _probeCodexPackage(pkgDir) {
|
|
113
|
+
if (!pkgDir) return null;
|
|
114
|
+
let stat = null;
|
|
115
|
+
try { stat = fs.statSync(pkgDir); } catch { return null; }
|
|
116
|
+
if (stat.isFile()) {
|
|
117
|
+
const packageRoot = _resolveNpmPackageFromBin(pkgDir);
|
|
118
|
+
if (packageRoot) {
|
|
119
|
+
const nativeHit = _resolveNativeCodexBinaryForPackage(packageRoot);
|
|
120
|
+
if (!nativeHit) return null;
|
|
121
|
+
if (_isNodeLoadable(pkgDir)) return { bin: pkgDir, native: false, source: 'npm-package' };
|
|
122
|
+
if (path.resolve(pkgDir) === path.resolve(nativeHit.bin)) return { bin: pkgDir, native: true, source: nativeHit.source };
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if (!_isNodeLoadable(pkgDir)) return { bin: pkgDir, native: true, source: 'direct' };
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const nativeHit = _resolveNativeCodexBinaryForPackage(pkgDir);
|
|
130
|
+
const binJs = path.join(pkgDir, 'bin', 'codex.js');
|
|
131
|
+
if (fs.existsSync(binJs)) {
|
|
132
|
+
if (!nativeHit) return null;
|
|
133
|
+
return { bin: binJs, native: false, source: 'npm-package' };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (nativeHit) return { bin: nativeHit.bin, native: true, source: nativeHit.source };
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function _platformTriple(platform = process.platform, arch = process.arch) {
|
|
141
|
+
if (platform === 'darwin' && arch === 'arm64') return 'aarch64-apple-darwin';
|
|
142
|
+
if (platform === 'darwin' && arch === 'x64') return 'x86_64-apple-darwin';
|
|
143
|
+
if ((platform === 'linux' || platform === 'android') && arch === 'arm64') return 'aarch64-unknown-linux-musl';
|
|
144
|
+
if ((platform === 'linux' || platform === 'android') && arch === 'x64') return 'x86_64-unknown-linux-musl';
|
|
145
|
+
if (platform === 'win32' && arch === 'arm64') return 'aarch64-pc-windows-msvc';
|
|
146
|
+
if (platform === 'win32' && arch === 'x64') return 'x86_64-pc-windows-msvc';
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function _normalizeWhichPath(p) {
|
|
151
|
+
if (!p) return null;
|
|
152
|
+
return isWin ? p : p.replace(/^\/([a-zA-Z])\//, (_, d) => d.toUpperCase() + ':/').replace(/\//g, path.sep);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function _isNodeLoadable(file) {
|
|
156
|
+
const ext = path.extname(file).toLowerCase();
|
|
157
|
+
return ext === '.js' || ext === '.mjs' || ext === '.cjs';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function _resolveNpmPackageFromBin(binPath) {
|
|
161
|
+
if (!binPath) return null;
|
|
162
|
+
let real = binPath;
|
|
163
|
+
try { real = fs.realpathSync(binPath); } catch {}
|
|
164
|
+
const normalized = real.replace(/\\/g, '/');
|
|
165
|
+
const marker = '/node_modules/@openai/codex/';
|
|
166
|
+
const idx = normalized.indexOf(marker);
|
|
167
|
+
if (idx >= 0) {
|
|
168
|
+
const root = normalized.slice(0, idx + marker.length - 1);
|
|
169
|
+
return root.split('/').join(path.sep);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const dir = path.dirname(real);
|
|
173
|
+
const candidates = [
|
|
174
|
+
path.join(dir, 'node_modules', '@openai', 'codex'),
|
|
175
|
+
path.join(dir, '..', 'lib', 'node_modules', '@openai', 'codex'),
|
|
176
|
+
];
|
|
177
|
+
for (const c of candidates) {
|
|
178
|
+
try { if (fs.existsSync(c)) return c; } catch {}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function _resolvePathHit(binPath) {
|
|
184
|
+
if (!binPath) return null;
|
|
185
|
+
const normalized = _normalizeWhichPath(binPath);
|
|
186
|
+
if (!normalized || !fs.existsSync(normalized)) return null;
|
|
187
|
+
|
|
188
|
+
const pkgDir = _resolveNpmPackageFromBin(normalized);
|
|
189
|
+
if (pkgDir) return _probeCodexPackage(pkgDir);
|
|
190
|
+
|
|
191
|
+
if (isWin) {
|
|
192
|
+
const ext = path.extname(normalized).toLowerCase();
|
|
193
|
+
if (ext === '.exe') return { bin: normalized, native: true, source: 'path' };
|
|
194
|
+
if (_isNodeLoadable(normalized)) return { bin: normalized, native: false, source: 'path-js' };
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
return { bin: normalized, native: !_isNodeLoadable(normalized), source: _isNodeLoadable(normalized) ? 'path-js' : 'path' };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function _findPathCodex(env) {
|
|
201
|
+
try {
|
|
202
|
+
const cmd = isWin ? 'where codex 2>NUL' : 'which codex 2>/dev/null';
|
|
203
|
+
const raw = _execSyncCapture(cmd, env);
|
|
204
|
+
for (const line of raw.split(/\r?\n/).map(s => s.trim()).filter(Boolean)) {
|
|
205
|
+
const hit = _resolvePathHit(line);
|
|
206
|
+
if (hit) return hit;
|
|
207
|
+
}
|
|
208
|
+
} catch { /* PATH probe is optional */ }
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function _findKnownNpmCodex(env) {
|
|
213
|
+
const prefixes = [
|
|
214
|
+
env.npm_config_prefix ? path.join(env.npm_config_prefix, 'node_modules', '@openai', 'codex') : '',
|
|
215
|
+
env.APPDATA ? path.join(env.APPDATA, 'npm', 'node_modules', '@openai', 'codex') : '',
|
|
216
|
+
'/usr/local/lib/node_modules/@openai/codex',
|
|
217
|
+
'/usr/lib/node_modules/@openai/codex',
|
|
218
|
+
'/opt/homebrew/lib/node_modules/@openai/codex',
|
|
219
|
+
path.join(path.dirname(process.execPath), '..', 'lib', 'node_modules', '@openai', 'codex'),
|
|
220
|
+
path.join(path.dirname(process.execPath), 'node_modules', '@openai', 'codex'),
|
|
221
|
+
path.join(MINIONS_DIR, 'node_modules', '@openai', 'codex'),
|
|
222
|
+
].filter(Boolean);
|
|
223
|
+
for (const pkg of prefixes) {
|
|
224
|
+
const found = _probeCodexPackage(pkg);
|
|
225
|
+
if (found) return found;
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
const globalRoot = _execSyncCapture('npm root -g', env).trim();
|
|
229
|
+
const found = _probeCodexPackage(path.join(globalRoot, '@openai', 'codex'));
|
|
230
|
+
if (found) return found;
|
|
231
|
+
} catch { /* optional */ }
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function _isCachedBinUsable(cached) {
|
|
236
|
+
if (!cached || typeof cached !== 'object') return false;
|
|
237
|
+
if (cached.version !== CAPS_SCHEMA_VERSION) return false;
|
|
238
|
+
if (typeof cached.codexBin !== 'string' || !cached.codexBin) return false;
|
|
239
|
+
try { if (!fs.existsSync(cached.codexBin)) return false; } catch { return false; }
|
|
240
|
+
if (cached.codexIsNative === false && !_isNodeLoadable(cached.codexBin)) return false;
|
|
241
|
+
if (cached.codexIsNative === false) {
|
|
242
|
+
const pkgDir = _resolveNpmPackageFromBin(cached.codexBin);
|
|
243
|
+
if (pkgDir && !_resolveNativeCodexBinaryForPackage(pkgDir)) return false;
|
|
244
|
+
}
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function resolveBinary({ env = process.env, config = null } = {}) {
|
|
249
|
+
const overridePath = config?.codex?.binary && config.codex.binary !== 'codex'
|
|
250
|
+
? config.codex.binary
|
|
251
|
+
: null;
|
|
252
|
+
if (overridePath && fs.existsSync(overridePath)) {
|
|
253
|
+
const pkgHit = _probeCodexPackage(overridePath);
|
|
254
|
+
if (pkgHit) return { bin: pkgHit.bin, native: pkgHit.native, leadingArgs: [] };
|
|
255
|
+
if (_resolveNpmPackageFromBin(overridePath) || _looksLikeCodexPackageDir(overridePath)) return null;
|
|
256
|
+
return { bin: overridePath, native: !_isNodeLoadable(overridePath), leadingArgs: [] };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const cached = _safeJson(CAPS_FILE);
|
|
260
|
+
if (_isCachedBinUsable(cached)) {
|
|
261
|
+
return { bin: cached.codexBin, native: !!cached.codexIsNative, leadingArgs: [] };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const found = _findPathCodex(env) || _findKnownNpmCodex(env);
|
|
265
|
+
if (!found) return null;
|
|
266
|
+
|
|
267
|
+
_safeWriteJson(CAPS_FILE, {
|
|
268
|
+
version: CAPS_SCHEMA_VERSION,
|
|
269
|
+
codexBin: found.bin,
|
|
270
|
+
codexIsNative: !!found.native,
|
|
271
|
+
source: found.source || 'unknown',
|
|
272
|
+
resolvedAt: new Date().toISOString(),
|
|
273
|
+
});
|
|
274
|
+
return { bin: found.bin, native: !!found.native, leadingArgs: [] };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function resolveModel(input) {
|
|
278
|
+
if (input == null || input === '') return undefined;
|
|
279
|
+
return String(input);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function modelLooksFamiliar(model) {
|
|
283
|
+
if (!model) return true;
|
|
284
|
+
const m = String(model).toLowerCase();
|
|
285
|
+
if (m.startsWith('gpt-')) return true;
|
|
286
|
+
if (m.startsWith('o3') || m.startsWith('o4')) return true;
|
|
287
|
+
if (m.includes('codex')) return true;
|
|
288
|
+
const catalog = module.exports._readCatalogIds();
|
|
289
|
+
if (catalog && catalog.has(m)) return true;
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function _readCatalogIds() {
|
|
294
|
+
const cached = _safeJson(MODELS_CACHE);
|
|
295
|
+
const list = cached && Array.isArray(cached.models) ? cached.models : null;
|
|
296
|
+
if (!list || list.length === 0) return null;
|
|
297
|
+
const ids = new Set();
|
|
298
|
+
for (const m of list) {
|
|
299
|
+
const id = m && (m.id || m.name);
|
|
300
|
+
if (id) ids.add(String(id).toLowerCase());
|
|
301
|
+
}
|
|
302
|
+
return ids.size > 0 ? ids : null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function _mapEffort(level) {
|
|
306
|
+
if (level == null || level === '') return undefined;
|
|
307
|
+
const s = String(level);
|
|
308
|
+
if (s === 'max') return 'xhigh';
|
|
309
|
+
return s;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function _normalizeConfigOverrides(configOverrides) {
|
|
313
|
+
if (!configOverrides) return [];
|
|
314
|
+
if (Array.isArray(configOverrides)) return configOverrides.filter(Boolean).map(String);
|
|
315
|
+
if (typeof configOverrides === 'object') {
|
|
316
|
+
return Object.entries(configOverrides)
|
|
317
|
+
.filter(([k, v]) => k && v !== undefined)
|
|
318
|
+
.map(([k, v]) => `${k}=${typeof v === 'string' ? JSON.stringify(v) : JSON.stringify(v)}`);
|
|
319
|
+
}
|
|
320
|
+
return [];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function buildArgs(opts = {}) {
|
|
324
|
+
const {
|
|
325
|
+
model,
|
|
326
|
+
effort,
|
|
327
|
+
sessionId,
|
|
328
|
+
outputLastMessage,
|
|
329
|
+
sandbox = 'workspace-write',
|
|
330
|
+
skipGitRepoCheck = true,
|
|
331
|
+
ignoreRules = true,
|
|
332
|
+
configOverrides,
|
|
333
|
+
} = opts;
|
|
334
|
+
|
|
335
|
+
const args = ['exec'];
|
|
336
|
+
if (sessionId) args.push('resume', String(sessionId));
|
|
337
|
+
|
|
338
|
+
args.push('--json', '--color', 'never');
|
|
339
|
+
if (model) args.push('--model', String(model));
|
|
340
|
+
if (sandbox) args.push('--sandbox', String(sandbox));
|
|
341
|
+
if (skipGitRepoCheck === true) args.push('--skip-git-repo-check');
|
|
342
|
+
if (ignoreRules === true) args.push('--ignore-rules');
|
|
343
|
+
if (outputLastMessage) args.push('--output-last-message', String(outputLastMessage));
|
|
344
|
+
|
|
345
|
+
const mappedEffort = _mapEffort(effort);
|
|
346
|
+
if (mappedEffort) args.push('--config', `model_reasoning_effort="${mappedEffort}"`);
|
|
347
|
+
|
|
348
|
+
for (const override of _normalizeConfigOverrides(configOverrides)) {
|
|
349
|
+
args.push('--config', override);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
args.push('-');
|
|
353
|
+
return args;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function buildSpawnFlags(opts = {}) {
|
|
357
|
+
const flags = ['--runtime', RUNTIME_NAME];
|
|
358
|
+
if (opts.maxTurns != null) flags.push('--max-turns', String(opts.maxTurns));
|
|
359
|
+
if (opts.model) flags.push('--model', String(opts.model));
|
|
360
|
+
if (opts.allowedTools) flags.push('--allowedTools', String(opts.allowedTools));
|
|
361
|
+
if (module.exports.capabilities.effortLevels && opts.effort) flags.push('--effort', String(opts.effort));
|
|
362
|
+
if (module.exports.capabilities.sessionResume && opts.sessionId) flags.push('--resume', String(opts.sessionId));
|
|
363
|
+
return flags;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function buildPrompt(promptText, sysPromptText, opts = {}) {
|
|
367
|
+
const user = promptText == null ? '' : String(promptText);
|
|
368
|
+
if (opts && opts.sessionId) return user;
|
|
369
|
+
if (sysPromptText == null || sysPromptText === '') return user;
|
|
370
|
+
return `<system>\n${String(sysPromptText)}\n</system>\n\n${user}`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function getUserAssetDirs({ homeDir = os.homedir() } = {}) {
|
|
374
|
+
return [
|
|
375
|
+
path.join(homeDir, '.codex'),
|
|
376
|
+
path.join(homeDir, '.agents'),
|
|
377
|
+
];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function getSkillRoots({ homeDir = os.homedir(), project = null } = {}) {
|
|
381
|
+
const roots = [
|
|
382
|
+
{ dir: path.join(homeDir, '.agents', 'skills'), scope: 'agent-skill' },
|
|
383
|
+
{ dir: path.join('/etc', 'codex', 'skills'), scope: 'codex-admin' },
|
|
384
|
+
];
|
|
385
|
+
if (project?.localPath) {
|
|
386
|
+
const projectName = project.name || path.basename(project.localPath);
|
|
387
|
+
roots.push({ dir: path.join(project.localPath, '.agents', 'skills'), scope: 'project', projectName });
|
|
388
|
+
}
|
|
389
|
+
return roots;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function getSkillWriteTargets({ homeDir = os.homedir(), project = null } = {}) {
|
|
393
|
+
const targets = { personal: path.join(homeDir, '.agents', 'skills') };
|
|
394
|
+
if (project?.localPath) targets.project = path.join(project.localPath, '.agents', 'skills');
|
|
395
|
+
return targets;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function getResumeSessionId({ agentId, branchName, agentsDir, maxAgeMs = 2 * 60 * 60 * 1000, logger = console } = {}) {
|
|
399
|
+
if (!agentId || agentId.startsWith('temp-') || !agentsDir) return null;
|
|
400
|
+
const sessionPath = path.join(agentsDir, agentId, 'session.json');
|
|
401
|
+
try {
|
|
402
|
+
const sessionFile = _safeJson(sessionPath);
|
|
403
|
+
if (!sessionFile?.sessionId || !sessionFile.savedAt) return null;
|
|
404
|
+
if (sessionFile.runtime && sessionFile.runtime !== RUNTIME_NAME) {
|
|
405
|
+
if (logger && typeof logger.info === 'function') {
|
|
406
|
+
logger.info(`Skipping resume for ${agentId}: runtime mismatch (session: ${sessionFile.runtime}, current: ${RUNTIME_NAME}) - clearing session.json`);
|
|
407
|
+
}
|
|
408
|
+
try { fs.unlinkSync(sessionPath); } catch {}
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
const sessionAge = Date.now() - new Date(sessionFile.savedAt).getTime();
|
|
412
|
+
const sameBranch = branchName && sessionFile.branch && sessionFile.branch === branchName;
|
|
413
|
+
if (sessionAge < maxAgeMs && sameBranch) return sessionFile.sessionId;
|
|
414
|
+
} catch (e) {
|
|
415
|
+
if (logger && typeof logger.warn === 'function') logger.warn('session resume lookup: ' + e.message);
|
|
416
|
+
}
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function saveSession({ agentId, dispatchId, branch, sessionId, agentsDir, now = ts, writeJson = safeWrite, logger = console } = {}) {
|
|
421
|
+
if (!sessionId || !agentId || agentId.startsWith('temp-') || !agentsDir) return false;
|
|
422
|
+
try {
|
|
423
|
+
writeJson(path.join(agentsDir, agentId, 'session.json'), {
|
|
424
|
+
sessionId,
|
|
425
|
+
dispatchId,
|
|
426
|
+
savedAt: typeof now === 'function' ? now() : new Date().toISOString(),
|
|
427
|
+
branch: branch || null,
|
|
428
|
+
runtime: RUNTIME_NAME,
|
|
429
|
+
});
|
|
430
|
+
return true;
|
|
431
|
+
} catch (err) {
|
|
432
|
+
if (logger && typeof logger.warn === 'function') logger.warn(`Session save: ${err.message}`);
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function detectPermissionGate(outputChunk) {
|
|
438
|
+
const lower = String(outputChunk || '').toLowerCase();
|
|
439
|
+
return /\b(approve|approval|allow|permission|trust this|continue\?)\b/.test(lower);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function getPromptDeliveryMode() {
|
|
443
|
+
return 'stdin';
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function usesSystemPromptFile() {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function _pickString(...values) {
|
|
451
|
+
for (const value of values) {
|
|
452
|
+
if (typeof value === 'string' && value) return value;
|
|
453
|
+
}
|
|
454
|
+
return '';
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function _eventType(obj) {
|
|
458
|
+
return String(obj?.type || obj?.event || obj?.event_type || obj?.data?.type || '');
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function _extractSessionId(obj) {
|
|
462
|
+
return _pickString(
|
|
463
|
+
obj?.session_id,
|
|
464
|
+
obj?.sessionId,
|
|
465
|
+
obj?.conversation_id,
|
|
466
|
+
obj?.conversationId,
|
|
467
|
+
obj?.thread_id,
|
|
468
|
+
obj?.threadId,
|
|
469
|
+
obj?.data?.session_id,
|
|
470
|
+
obj?.data?.sessionId,
|
|
471
|
+
obj?.data?.conversation_id,
|
|
472
|
+
obj?.data?.thread_id,
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function _extractModel(obj) {
|
|
477
|
+
return _pickString(obj?.model, obj?.data?.model, obj?.metadata?.model, obj?.response?.model);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function _extractText(obj) {
|
|
481
|
+
const type = _eventType(obj);
|
|
482
|
+
const item = obj?.item || obj?.data?.item;
|
|
483
|
+
if (item && typeof item === 'object') {
|
|
484
|
+
const itemType = String(item.type || '');
|
|
485
|
+
if (/agent_message|assistant_message|message|output_text/i.test(itemType)) {
|
|
486
|
+
const text = _pickString(item.text, item.content, item.message, item.output);
|
|
487
|
+
if (text) return text;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (/agent_message|assistant_message|message|output_text|task_complete|final/i.test(type)) {
|
|
491
|
+
return _pickString(
|
|
492
|
+
obj?.text,
|
|
493
|
+
obj?.message,
|
|
494
|
+
obj?.content,
|
|
495
|
+
obj?.summary,
|
|
496
|
+
obj?.final_message,
|
|
497
|
+
obj?.finalMessage,
|
|
498
|
+
obj?.data?.text,
|
|
499
|
+
obj?.data?.message,
|
|
500
|
+
obj?.data?.content,
|
|
501
|
+
obj?.data?.summary,
|
|
502
|
+
obj?.data?.final_message,
|
|
503
|
+
obj?.data?.finalMessage,
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
if (obj?.type === 'result' || obj?.type === 'turn.completed') {
|
|
507
|
+
return _pickString(obj?.output, obj?.text, obj?.message, obj?.data?.output, obj?.data?.text, obj?.data?.message);
|
|
508
|
+
}
|
|
509
|
+
return '';
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function _extractDelta(obj) {
|
|
513
|
+
const type = _eventType(obj);
|
|
514
|
+
if (!/delta|chunk/i.test(type)) return '';
|
|
515
|
+
return _pickString(
|
|
516
|
+
obj?.delta,
|
|
517
|
+
obj?.text_delta,
|
|
518
|
+
obj?.content_delta,
|
|
519
|
+
obj?.data?.delta,
|
|
520
|
+
obj?.data?.text_delta,
|
|
521
|
+
obj?.data?.content_delta,
|
|
522
|
+
obj?.data?.deltaContent,
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function _extractToolUse(obj) {
|
|
527
|
+
const type = _eventType(obj);
|
|
528
|
+
const item = obj?.item || obj?.data?.item;
|
|
529
|
+
const itemType = String(item?.type || '');
|
|
530
|
+
if (!/tool|function|command|web_search/i.test(type) && !/tool|command|web_search/i.test(itemType)) return null;
|
|
531
|
+
const data = item || obj.data || obj;
|
|
532
|
+
const name = _pickString(
|
|
533
|
+
data.tool_name,
|
|
534
|
+
data.toolName,
|
|
535
|
+
data.name,
|
|
536
|
+
data.command,
|
|
537
|
+
data.tool,
|
|
538
|
+
data.server && data.tool ? `${data.server}.${data.tool}` : '',
|
|
539
|
+
data.function?.name,
|
|
540
|
+
);
|
|
541
|
+
if (!name) return null;
|
|
542
|
+
const input = data.arguments || data.args || data.input || data.function?.arguments || {};
|
|
543
|
+
return { name, input };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function _usageFrom(obj) {
|
|
547
|
+
const source = obj?.usage || obj?.data?.usage || obj?.response?.usage || null;
|
|
548
|
+
if (!source || typeof source !== 'object') return null;
|
|
549
|
+
return {
|
|
550
|
+
costUsd: source.cost_usd ?? source.costUsd ?? null,
|
|
551
|
+
inputTokens: source.input_tokens ?? source.inputTokens ?? source.prompt_tokens ?? null,
|
|
552
|
+
outputTokens: source.output_tokens ?? source.outputTokens ?? source.completion_tokens ?? null,
|
|
553
|
+
cacheRead: source.cached_input_tokens ?? source.cache_read_input_tokens ?? source.cacheReadInputTokens ?? null,
|
|
554
|
+
cacheCreation: source.cache_creation_input_tokens ?? source.cacheCreationInputTokens ?? null,
|
|
555
|
+
durationMs: source.duration_ms ?? source.durationMs ?? obj?.duration_ms ?? obj?.durationMs ?? 0,
|
|
556
|
+
numTurns: source.num_turns ?? source.numTurns ?? null,
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
function parseOutput(raw, { maxTextLength = 0 } = {}) {
|
|
561
|
+
const safeRaw = raw == null ? '' : String(raw);
|
|
562
|
+
if (!safeRaw) return { text: '', usage: null, sessionId: null, model: null };
|
|
563
|
+
|
|
564
|
+
let text = '';
|
|
565
|
+
let deltaText = '';
|
|
566
|
+
let usage = null;
|
|
567
|
+
let sessionId = null;
|
|
568
|
+
let model = null;
|
|
569
|
+
|
|
570
|
+
for (const rawLine of safeRaw.split('\n')) {
|
|
571
|
+
const line = rawLine.trim();
|
|
572
|
+
if (!line) continue;
|
|
573
|
+
if (!line.startsWith('{')) {
|
|
574
|
+
text = line;
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
let obj;
|
|
578
|
+
try { obj = JSON.parse(line); } catch { continue; }
|
|
579
|
+
const id = _extractSessionId(obj);
|
|
580
|
+
if (id) sessionId = id;
|
|
581
|
+
const m = _extractModel(obj);
|
|
582
|
+
if (m && !model) model = m;
|
|
583
|
+
const u = _usageFrom(obj);
|
|
584
|
+
if (u) usage = u;
|
|
585
|
+
const delta = _extractDelta(obj);
|
|
586
|
+
if (delta) deltaText += delta;
|
|
587
|
+
const eventText = _extractText(obj);
|
|
588
|
+
if (eventText) {
|
|
589
|
+
text = eventText;
|
|
590
|
+
deltaText = '';
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const finalText = text || deltaText;
|
|
595
|
+
return {
|
|
596
|
+
text: maxTextLength && finalText.length > maxTextLength ? finalText.slice(-maxTextLength) : finalText,
|
|
597
|
+
usage,
|
|
598
|
+
sessionId,
|
|
599
|
+
model,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function parseStreamChunk(line) {
|
|
604
|
+
const trimmed = line == null ? '' : String(line).trim();
|
|
605
|
+
if (!trimmed || !trimmed.startsWith('{')) return null;
|
|
606
|
+
try { return JSON.parse(trimmed); } catch { return null; }
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function _collectErrorSignal(rawOutput) {
|
|
610
|
+
const text = rawOutput == null ? '' : String(rawOutput);
|
|
611
|
+
if (!text) return '';
|
|
612
|
+
const signals = [];
|
|
613
|
+
let sawJsonLine = false;
|
|
614
|
+
for (const rawLine of text.split('\n')) {
|
|
615
|
+
const line = rawLine.trim();
|
|
616
|
+
if (!line) continue;
|
|
617
|
+
if (!line.startsWith('{')) {
|
|
618
|
+
signals.push(line);
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
let obj;
|
|
622
|
+
try { obj = JSON.parse(line); } catch { continue; }
|
|
623
|
+
sawJsonLine = true;
|
|
624
|
+
const type = _eventType(obj).toLowerCase();
|
|
625
|
+
const isError = type.includes('error') || obj.error != null || obj.data?.error != null || obj.is_error === true;
|
|
626
|
+
if (!isError) continue;
|
|
627
|
+
for (const value of [
|
|
628
|
+
obj.error,
|
|
629
|
+
obj.message,
|
|
630
|
+
obj.stderr,
|
|
631
|
+
obj.data?.error,
|
|
632
|
+
obj.data?.message,
|
|
633
|
+
obj.data?.stderr,
|
|
634
|
+
]) {
|
|
635
|
+
if (typeof value === 'string' && value.trim()) signals.push(value.trim());
|
|
636
|
+
else if (value && typeof value === 'object' && typeof value.message === 'string') signals.push(value.message);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
if (signals.length > 0) return signals.join('\n');
|
|
640
|
+
return sawJsonLine ? '' : text;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function _extractInvalidModelName(text) {
|
|
644
|
+
if (!text) return null;
|
|
645
|
+
const patterns = [
|
|
646
|
+
/(?:unknown|invalid)\s+model(?:\s+id)?\s*[:=]?\s*['"`]?([A-Za-z0-9._\/-]+)['"`]?/i,
|
|
647
|
+
/model\s+['"`]([^'"`]+)['"`]\s+(?:not\s+found|is\s+invalid|is\s+unknown|invalid)/i,
|
|
648
|
+
/model\s+(?:not\s+found|invalid|unknown)\s*[:=]?\s*['"`]?([A-Za-z0-9._\/-]+)['"`]?/i,
|
|
649
|
+
/"model"\s*:\s*"([^"]+)"/i,
|
|
650
|
+
];
|
|
651
|
+
for (const re of patterns) {
|
|
652
|
+
const m = text.match(re);
|
|
653
|
+
if (m && m[1]) return m[1];
|
|
654
|
+
}
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function parseError(rawOutput) {
|
|
659
|
+
const text = _collectErrorSignal(rawOutput);
|
|
660
|
+
if (!text) return { message: '', code: null, retriable: true };
|
|
661
|
+
const lower = text.toLowerCase();
|
|
662
|
+
|
|
663
|
+
const hasAuth = /not authenticated|login required|please.*log.*in|api key.*invalid|invalid api key|\bunauthorized\b|\b401\b|\b403\b/i.test(text);
|
|
664
|
+
if (hasAuth) {
|
|
665
|
+
return { message: 'Codex authentication failed - run `codex login` or configure an OpenAI API key, then try again.', code: 'auth-failure', retriable: false };
|
|
666
|
+
}
|
|
667
|
+
if (/context window|context.*length.*exceeded|token limit|prompt.*too long|conversation.*too long/i.test(text)) {
|
|
668
|
+
return { message: 'Codex context window exhausted', code: 'context-limit', retriable: false };
|
|
669
|
+
}
|
|
670
|
+
if (/missing optional dependency\s+@openai\/codex-|reinstall codex|unsupported platform/i.test(text)) {
|
|
671
|
+
return { message: 'Codex installation is incomplete for this platform. Reinstall `@openai/codex` or install the native Codex CLI, then restart Minions.', code: 'config-error', retriable: false };
|
|
672
|
+
}
|
|
673
|
+
if (/unknown\s+model|model\s+not\s+found|invalid\s+model|model_not_found|400[^a-z]+(?:bad\s+request|invalid|model)/i.test(text)) {
|
|
674
|
+
const name = _extractInvalidModelName(text) || 'unknown';
|
|
675
|
+
return { message: `Model "${name}" not available for runtime codex. Configure a valid model in Settings -> Engine.`, code: 'model-unavailable', retriable: false };
|
|
676
|
+
}
|
|
677
|
+
if (/overloaded|service[_ ]unavailable|temporarily unavailable|\b503\b|rate limit|too many requests|\b429\b/i.test(text)) {
|
|
678
|
+
return { message: text, code: 'model-unavailable', retriable: true };
|
|
679
|
+
}
|
|
680
|
+
if (/sandbox|permission denied|operation not permitted|not allowed|approval required|read-only/i.test(lower)) {
|
|
681
|
+
return { message: text, code: 'permission-blocked', retriable: false };
|
|
682
|
+
}
|
|
683
|
+
if (/internal error|panic|uncaught|codex.*crashed|fatal: codex/i.test(lower)) {
|
|
684
|
+
return { message: text, code: 'crash', retriable: true };
|
|
685
|
+
}
|
|
686
|
+
return { message: '', code: null, retriable: true };
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function _runtimeFailureClass(code) {
|
|
690
|
+
if (code === 'config-error') return FAILURE_CLASS.CONFIG_ERROR;
|
|
691
|
+
if (code === 'auth-failure' || code === 'permission-blocked') return FAILURE_CLASS.PERMISSION_BLOCKED;
|
|
692
|
+
if (code === 'context-limit') return FAILURE_CLASS.OUT_OF_CONTEXT;
|
|
693
|
+
if (code === 'model-unavailable') return FAILURE_CLASS.MODEL_UNAVAILABLE;
|
|
694
|
+
if (code === 'crash') return FAILURE_CLASS.SPAWN_ERROR;
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function classifyFailure({ code, stdout = '', stderr = '', fallback } = {}) {
|
|
699
|
+
if (code === 78) return { failureClass: FAILURE_CLASS.CONFIG_ERROR, retryable: false, message: 'Codex configuration error' };
|
|
700
|
+
const parsed = parseError(`${stdout || ''}\n${stderr || ''}`);
|
|
701
|
+
const runtimeClass = parsed.code ? _runtimeFailureClass(parsed.code) : null;
|
|
702
|
+
if (runtimeClass) return { failureClass: runtimeClass, retryable: parsed.retriable !== false, message: parsed.message || '' };
|
|
703
|
+
const fallbackClass = typeof fallback === 'function' ? fallback(code, stdout, stderr) : FAILURE_CLASS.UNKNOWN;
|
|
704
|
+
return { failureClass: fallbackClass, retryable: parsed.retriable !== false, message: parsed.message || '' };
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function _normalizeModelEntry(entry) {
|
|
708
|
+
if (!entry) return null;
|
|
709
|
+
if (typeof entry === 'string') return { id: entry, name: entry, provider: 'openai' };
|
|
710
|
+
if (typeof entry !== 'object') return null;
|
|
711
|
+
const id = entry.id || entry.name || entry.model || entry.slug;
|
|
712
|
+
if (!id) return null;
|
|
713
|
+
return {
|
|
714
|
+
id: String(id),
|
|
715
|
+
name: String(entry.name || entry.display_name || entry.label || id),
|
|
716
|
+
provider: String(entry.provider || entry.provider_id || entry.vendor || 'openai'),
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function _extractModelsFromCatalog(catalog) {
|
|
721
|
+
if (!catalog || typeof catalog !== 'object') return null;
|
|
722
|
+
const candidates = [
|
|
723
|
+
catalog.models,
|
|
724
|
+
catalog.data,
|
|
725
|
+
catalog.catalog,
|
|
726
|
+
catalog.available_models,
|
|
727
|
+
catalog.availableModels,
|
|
728
|
+
];
|
|
729
|
+
for (const c of candidates) {
|
|
730
|
+
if (Array.isArray(c)) {
|
|
731
|
+
const models = c.map(_normalizeModelEntry).filter(Boolean);
|
|
732
|
+
return models.length > 0 ? models : null;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
const values = Object.values(catalog).filter(v => v && typeof v === 'object');
|
|
736
|
+
const models = values.map(_normalizeModelEntry).filter(Boolean);
|
|
737
|
+
return models.length > 0 ? models : null;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
async function listModels({ env = process.env, timeoutMs = 10000 } = {}) {
|
|
741
|
+
const resolved = resolveBinary({ env });
|
|
742
|
+
if (!resolved) return null;
|
|
743
|
+
let raw = '';
|
|
744
|
+
try {
|
|
745
|
+
raw = _execFileCapture(resolved.bin, [...(resolved.leadingArgs || []), 'debug', 'models', '--bundled'], {
|
|
746
|
+
env,
|
|
747
|
+
timeoutMs,
|
|
748
|
+
native: resolved.native,
|
|
749
|
+
});
|
|
750
|
+
} catch {
|
|
751
|
+
return null;
|
|
752
|
+
}
|
|
753
|
+
try {
|
|
754
|
+
const parsed = JSON.parse(raw);
|
|
755
|
+
return _extractModelsFromCatalog(parsed);
|
|
756
|
+
} catch {
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function createStreamConsumer(ctx) {
|
|
762
|
+
let deltaText = '';
|
|
763
|
+
let terminalText = '';
|
|
764
|
+
|
|
765
|
+
function consume(obj) {
|
|
766
|
+
if (!obj || typeof obj !== 'object') return;
|
|
767
|
+
const id = _extractSessionId(obj);
|
|
768
|
+
if (id) ctx.setSessionId(id);
|
|
769
|
+
|
|
770
|
+
const u = _usageFrom(obj);
|
|
771
|
+
if (u) ctx.setUsage(u);
|
|
772
|
+
|
|
773
|
+
const type = _eventType(obj);
|
|
774
|
+
if (/reasoning|thinking/i.test(type)) ctx.notifyThinking();
|
|
775
|
+
|
|
776
|
+
const toolUse = _extractToolUse(obj);
|
|
777
|
+
if (toolUse) ctx.pushToolUse(toolUse.name, toolUse.input);
|
|
778
|
+
|
|
779
|
+
const delta = _extractDelta(obj);
|
|
780
|
+
if (delta) {
|
|
781
|
+
deltaText += delta;
|
|
782
|
+
ctx.pushText(deltaText);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const eventText = _extractText(obj);
|
|
786
|
+
if (eventText) {
|
|
787
|
+
terminalText = eventText;
|
|
788
|
+
deltaText = '';
|
|
789
|
+
if (/complete|completed|final|result/i.test(type)) ctx.setText(eventText);
|
|
790
|
+
else ctx.pushText(eventText);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function reset() {
|
|
795
|
+
deltaText = '';
|
|
796
|
+
terminalText = '';
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return { consume, reset };
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const capabilities = {
|
|
803
|
+
streaming: true,
|
|
804
|
+
sessionResume: true,
|
|
805
|
+
midRunSessionId: false,
|
|
806
|
+
systemPromptFile: false,
|
|
807
|
+
effortLevels: true,
|
|
808
|
+
costTracking: false,
|
|
809
|
+
modelShorthands: false,
|
|
810
|
+
modelDiscovery: true,
|
|
811
|
+
promptViaArg: false,
|
|
812
|
+
budgetCap: false,
|
|
813
|
+
bareMode: false,
|
|
814
|
+
fallbackModel: false,
|
|
815
|
+
sessionPersistenceControl: false,
|
|
816
|
+
resumePromptCarryover: true,
|
|
817
|
+
streamConsumer: true,
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
const INSTALL_HINT = 'install Codex CLI with `npm install -g @openai/codex` or `brew install --cask codex`, then run `codex login`.';
|
|
821
|
+
const PERMISSION_BYPASS_FLAGS = ['--sandbox'];
|
|
822
|
+
|
|
823
|
+
module.exports = {
|
|
824
|
+
name: RUNTIME_NAME,
|
|
825
|
+
capabilities,
|
|
826
|
+
resolveBinary,
|
|
827
|
+
capsFile: CAPS_FILE,
|
|
828
|
+
listModels,
|
|
829
|
+
modelsCache: MODELS_CACHE,
|
|
830
|
+
spawnScript: path.join(ENGINE_DIR, 'spawn-agent.js'),
|
|
831
|
+
installHint: INSTALL_HINT,
|
|
832
|
+
buildSpawnFlags,
|
|
833
|
+
buildArgs,
|
|
834
|
+
buildPrompt,
|
|
835
|
+
getUserAssetDirs,
|
|
836
|
+
getSkillRoots,
|
|
837
|
+
getSkillWriteTargets,
|
|
838
|
+
getResumeSessionId,
|
|
839
|
+
saveSession,
|
|
840
|
+
detectPermissionGate,
|
|
841
|
+
getPromptDeliveryMode,
|
|
842
|
+
usesSystemPromptFile,
|
|
843
|
+
classifyFailure,
|
|
844
|
+
resolveModel,
|
|
845
|
+
modelLooksFamiliar,
|
|
846
|
+
parseOutput,
|
|
847
|
+
parseStreamChunk,
|
|
848
|
+
parseError,
|
|
849
|
+
createStreamConsumer,
|
|
850
|
+
permissionBypassFlags: PERMISSION_BYPASS_FLAGS,
|
|
851
|
+
_platformTriple,
|
|
852
|
+
_probeCodexPackage,
|
|
853
|
+
_resolvePathHit,
|
|
854
|
+
_isCachedBinUsable,
|
|
855
|
+
_resolveNativeCodexBinaryForPackage,
|
|
856
|
+
_mapEffort,
|
|
857
|
+
_normalizeConfigOverrides,
|
|
858
|
+
_extractModelsFromCatalog,
|
|
859
|
+
_readCatalogIds,
|
|
860
|
+
_extractInvalidModelName,
|
|
861
|
+
CAPS_SCHEMA_VERSION,
|
|
862
|
+
};
|