negotium 0.1.28 → 0.1.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/dist/agent-helpers.js +972 -506
- package/dist/agent-helpers.js.map +18 -17
- package/dist/background-bash.js +60 -5
- package/dist/background-bash.js.map +4 -4
- package/dist/{chunk-7azk83mw.js → chunk-en2s6xnh.js} +60 -5
- package/dist/chunk-en2s6xnh.js.map +18 -0
- package/dist/cron.js +3797 -3224
- package/dist/cron.js.map +24 -24
- package/dist/hosted-agent.js +527 -71
- package/dist/hosted-agent.js.map +13 -12
- package/dist/main.js +2759 -1665
- package/dist/main.js.map +30 -30
- package/dist/mcp-factories.js +58 -3
- package/dist/mcp-factories.js.map +4 -4
- package/dist/prompts.js +58 -3
- package/dist/prompts.js.map +4 -4
- package/dist/registry.js +1 -1
- package/dist/rollout.js +1 -1
- package/dist/runtime/scripts/browser-passkey-policy.mjs +15 -4
- package/dist/runtime/scripts/browser-vault-transform.mjs +5 -1
- package/dist/runtime/scripts/mcp-patchright-http.mjs +241 -26
- package/dist/runtime/src/agents/api-topic-agent-switch.ts +1 -1
- package/dist/runtime/src/agents/auth-check.ts +36 -10
- package/dist/runtime/src/agents/claude-provider.ts +6 -11
- package/dist/runtime/src/agents/codex-native-multi-agent.ts +159 -42
- package/dist/runtime/src/agents/codex-provider.ts +198 -8
- package/dist/runtime/src/agents/maestro-provider.ts +26 -0
- package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +1 -1
- package/dist/runtime/src/agents/rollout/codex.ts +198 -2
- package/dist/runtime/src/agents/rollout/index.ts +6 -0
- package/dist/runtime/src/agents/self-config-core.ts +1 -1
- package/dist/runtime/src/agents/tool-format.ts +228 -15
- package/dist/runtime/src/agents/topic-agent-switch.ts +1 -1
- package/dist/runtime/src/application/switch-topic-model.ts +1 -1
- package/dist/runtime/src/bus.ts +21 -3
- package/dist/runtime/src/mcp/browser-sse-proxy-server.ts +48 -0
- package/dist/runtime/src/platform/config.ts +80 -4
- package/dist/runtime/src/platform/mcp-config.ts +13 -9
- package/dist/runtime/src/platform/playwright/manager.ts +24 -14
- package/dist/runtime/src/runtime/turn-runner.ts +11 -5
- package/dist/runtime/src/types/api.ts +2 -0
- package/dist/runtime/src/types.ts +2 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +58 -3
- package/dist/runtime-helpers.js.map +4 -4
- package/dist/storage.js.map +1 -1
- package/dist/types/packages/core/src/agents/auth-check.d.ts +3 -2
- package/dist/types/packages/core/src/agents/codex-native-multi-agent.d.ts +6 -4
- package/dist/types/packages/core/src/agents/maestro-provider.d.ts +13 -0
- package/dist/types/packages/core/src/agents/rollout/codex.d.ts +24 -0
- package/dist/types/packages/core/src/agents/rollout/index.d.ts +2 -2
- package/dist/types/packages/core/src/agents/tool-format.d.ts +16 -9
- package/dist/types/packages/core/src/bus.d.ts +2 -2
- package/dist/types/packages/core/src/platform/config.d.ts +13 -1
- package/dist/types/packages/core/src/types/api.d.ts +2 -0
- package/dist/types/packages/core/src/types.d.ts +2 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/vault.js +58 -3
- package/dist/vault.js.map +4 -4
- package/install-browser-rs.mjs +77 -0
- package/package.json +6 -3
- package/dist/chunk-7azk83mw.js.map +0 -18
package/dist/agent-helpers.js
CHANGED
|
@@ -18,14 +18,23 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
18
18
|
var __require = import.meta.require;
|
|
19
19
|
|
|
20
20
|
// ../../packages/core/src/agents/auth-check.ts
|
|
21
|
-
import { execFileSync } from "child_process";
|
|
21
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
22
22
|
import { existsSync as existsSync2 } from "fs";
|
|
23
23
|
import { homedir as homedir2, platform } from "os";
|
|
24
|
-
import { join as
|
|
24
|
+
import { join as join3 } from "path";
|
|
25
25
|
|
|
26
26
|
// ../../packages/core/src/platform/config.ts
|
|
27
|
+
import { execFileSync } from "child_process";
|
|
27
28
|
import { randomBytes } from "crypto";
|
|
28
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
accessSync,
|
|
31
|
+
chmodSync,
|
|
32
|
+
constants,
|
|
33
|
+
existsSync,
|
|
34
|
+
mkdirSync,
|
|
35
|
+
readFileSync,
|
|
36
|
+
writeFileSync
|
|
37
|
+
} from "fs";
|
|
29
38
|
import { homedir } from "os";
|
|
30
39
|
import { dirname, join, resolve } from "path";
|
|
31
40
|
import { fileURLToPath } from "url";
|
|
@@ -119,7 +128,8 @@ var CONTEXTS_DIR = resolve(WORKSPACE_DIR, "contexts");
|
|
|
119
128
|
var BROWSER_PROFILES_DIR = resolve(WORKSPACE_DIR, "browser-profiles");
|
|
120
129
|
var DM_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "dm");
|
|
121
130
|
var SESSION_WORKSPACE_DIR = resolve(WORKSPACE_DIR, "sessions");
|
|
122
|
-
var
|
|
131
|
+
var CLAUDE_EXECUTABLE_ENV = envText("NEGOTIUM_CLAUDE_EXECUTABLE");
|
|
132
|
+
var CLAUDE_EXECUTABLE = CLAUDE_EXECUTABLE_ENV ? resolve(CLAUDE_EXECUTABLE_ENV) : undefined;
|
|
123
133
|
function resolveBrowserMcpBin(envValue) {
|
|
124
134
|
const override = envValue?.trim();
|
|
125
135
|
if (override) {
|
|
@@ -132,10 +142,55 @@ function resolveBrowserMcpBin(envValue) {
|
|
|
132
142
|
}
|
|
133
143
|
var PATCHRIGHT_MCP_BIN = resolve(PROJECT_ROOT, "scripts/mcp-patchright-http.mjs");
|
|
134
144
|
var PLAYWRIGHT_MCP_BIN = resolveBrowserMcpBin(envText("NEGOTIUM_BROWSER_MCP_BIN"));
|
|
145
|
+
var BROWSER_RS_VERSION = "v0.1.12";
|
|
146
|
+
var BROWSER_RS_MIN_SECURE_VERSION = "0.1.12";
|
|
147
|
+
function versionAtLeast(actualVersion, minimumVersion) {
|
|
148
|
+
const actual = actualVersion.split(".").map(Number);
|
|
149
|
+
const minimum = minimumVersion.split(".").map(Number);
|
|
150
|
+
if (actual.some(Number.isNaN) || minimum.some(Number.isNaN))
|
|
151
|
+
return false;
|
|
152
|
+
for (let index = 0;index < minimum.length; index += 1) {
|
|
153
|
+
if ((actual[index] ?? 0) > (minimum[index] ?? 0))
|
|
154
|
+
return true;
|
|
155
|
+
if ((actual[index] ?? 0) < (minimum[index] ?? 0))
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
function browserRsMeetsMinimumVersion(candidate) {
|
|
161
|
+
try {
|
|
162
|
+
const output = execFileSync(candidate, ["--version"], {
|
|
163
|
+
encoding: "utf8",
|
|
164
|
+
timeout: 2000,
|
|
165
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
166
|
+
}).trim();
|
|
167
|
+
const match = output.match(/^browser-rs (\d+)\.(\d+)\.(\d+)$/);
|
|
168
|
+
if (!match)
|
|
169
|
+
return false;
|
|
170
|
+
return versionAtLeast(match.slice(1).join("."), BROWSER_RS_MIN_SECURE_VERSION);
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function resolveBrowserRsBin(envValue) {
|
|
176
|
+
const override = envValue?.trim();
|
|
177
|
+
if (!override && !versionAtLeast(BROWSER_RS_VERSION.replace(/^v/, ""), BROWSER_RS_MIN_SECURE_VERSION)) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const candidate = override ? resolve(override) : resolve(STATE_DIR, "bin", "browser-rs", BROWSER_RS_VERSION, "browser-rs");
|
|
181
|
+
try {
|
|
182
|
+
accessSync(candidate, constants.X_OK);
|
|
183
|
+
return browserRsMeetsMinimumVersion(candidate) ? candidate : undefined;
|
|
184
|
+
} catch {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
var BROWSER_RS_BIN = resolveBrowserRsBin(envText("NEGOTIUM_BROWSER_RS_BIN"));
|
|
135
189
|
var TSX_BIN = resolveDependencyBin("tsx");
|
|
136
190
|
var TSCONFIG_PATH = resolve(PROJECT_ROOT, "tsconfig.json");
|
|
137
191
|
var SESSION_COMM_SERVER = resolve(PROJECT_ROOT, "src/mcp/session-comm/server.ts");
|
|
138
192
|
var TASK_SERVER = resolve(PROJECT_ROOT, "src/mcp/task-server.ts");
|
|
193
|
+
var BROWSER_MCP_SSE_PROXY_SERVER = resolve(PROJECT_ROOT, "src/mcp/browser-sse-proxy-server.ts");
|
|
139
194
|
var CANONICAL_MCP_PROXY_SERVER = resolve(PROJECT_ROOT, "src/mcp/canonical-proxy-server.ts");
|
|
140
195
|
var WIKI_SERVER = resolve(PROJECT_ROOT, "src/mcp/wiki-server.ts");
|
|
141
196
|
var TOKEN_STATS_SERVER = resolve(PROJECT_ROOT, "src/mcp/token-stats-server.ts");
|
|
@@ -253,6 +308,231 @@ function codexAuthFilePath() {
|
|
|
253
308
|
return process.env.NEGOTIUM_CODEX_AUTH_FILE || join(homedir(), ".codex", "auth.json");
|
|
254
309
|
}
|
|
255
310
|
|
|
311
|
+
// ../../packages/core/src/storage/vault.ts
|
|
312
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
313
|
+
import { join as join2 } from "path";
|
|
314
|
+
|
|
315
|
+
// ../../packages/core/src/storage/sqlite.ts
|
|
316
|
+
var isBun = typeof process.versions.bun === "string";
|
|
317
|
+
var Database;
|
|
318
|
+
if (isBun) {
|
|
319
|
+
({ Database } = await import("bun:sqlite"));
|
|
320
|
+
} else {
|
|
321
|
+
const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
|
|
322
|
+
const { DatabaseSync } = await import(nodeSqliteSpecifier);
|
|
323
|
+
|
|
324
|
+
class NodeDatabase {
|
|
325
|
+
#db;
|
|
326
|
+
constructor(path, options = {}) {
|
|
327
|
+
this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
|
|
328
|
+
}
|
|
329
|
+
query(sql) {
|
|
330
|
+
return this.#db.prepare(sql);
|
|
331
|
+
}
|
|
332
|
+
prepare(sql) {
|
|
333
|
+
return this.#db.prepare(sql);
|
|
334
|
+
}
|
|
335
|
+
exec(sql) {
|
|
336
|
+
this.#db.exec(sql);
|
|
337
|
+
}
|
|
338
|
+
run(sql, ...params) {
|
|
339
|
+
return this.#db.prepare(sql).run(...params);
|
|
340
|
+
}
|
|
341
|
+
transaction(fn) {
|
|
342
|
+
const run = (begin) => (...args) => {
|
|
343
|
+
this.#db.exec(begin);
|
|
344
|
+
try {
|
|
345
|
+
const result = fn(...args);
|
|
346
|
+
this.#db.exec("COMMIT");
|
|
347
|
+
return result;
|
|
348
|
+
} catch (err) {
|
|
349
|
+
this.#db.exec("ROLLBACK");
|
|
350
|
+
throw err;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
const tx = run("BEGIN");
|
|
354
|
+
tx.deferred = run("BEGIN DEFERRED");
|
|
355
|
+
tx.immediate = run("BEGIN IMMEDIATE");
|
|
356
|
+
tx.exclusive = run("BEGIN EXCLUSIVE");
|
|
357
|
+
return tx;
|
|
358
|
+
}
|
|
359
|
+
close() {
|
|
360
|
+
this.#db.close();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
Database = NodeDatabase;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ../../packages/core/src/storage/vault-crypto.ts
|
|
367
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes2 } from "crypto";
|
|
368
|
+
var ENVELOPE_PREFIX = "otium-vault:v1:";
|
|
369
|
+
var IV_BYTES = 12;
|
|
370
|
+
var KEY_BYTES = 32;
|
|
371
|
+
function encryptionKey(masterKey = VAULT_MASTER_KEY) {
|
|
372
|
+
return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
|
|
373
|
+
}
|
|
374
|
+
function aad(userId, key) {
|
|
375
|
+
return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
|
|
376
|
+
}
|
|
377
|
+
function isEncryptedVaultValue(value) {
|
|
378
|
+
return value.startsWith(ENVELOPE_PREFIX);
|
|
379
|
+
}
|
|
380
|
+
function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
|
|
381
|
+
const iv = randomBytes2(IV_BYTES);
|
|
382
|
+
const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
383
|
+
cipher.setAAD(aad(userId, key));
|
|
384
|
+
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
385
|
+
const tag = cipher.getAuthTag();
|
|
386
|
+
return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
387
|
+
}
|
|
388
|
+
function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
|
|
389
|
+
if (!isEncryptedVaultValue(storedValue)) {
|
|
390
|
+
return { value: storedValue, legacyPlaintext: true };
|
|
391
|
+
}
|
|
392
|
+
const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
|
|
393
|
+
const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
|
|
394
|
+
if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
|
|
395
|
+
throw new Error("Invalid encrypted vault value");
|
|
396
|
+
}
|
|
397
|
+
const iv = Buffer.from(ivPart, "base64url");
|
|
398
|
+
const ciphertext = Buffer.from(ciphertextPart, "base64url");
|
|
399
|
+
const tag = Buffer.from(tagPart, "base64url");
|
|
400
|
+
if (iv.length !== IV_BYTES || tag.length !== 16) {
|
|
401
|
+
throw new Error("Invalid encrypted vault value");
|
|
402
|
+
}
|
|
403
|
+
const decipher = createDecipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
404
|
+
decipher.setAAD(aad(userId, key));
|
|
405
|
+
decipher.setAuthTag(tag);
|
|
406
|
+
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
407
|
+
return { value: plaintext.toString("utf8"), legacyPlaintext: false };
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// ../../packages/core/src/storage/vault.ts
|
|
411
|
+
var vaultDb;
|
|
412
|
+
var vaultMasterKey = VAULT_MASTER_KEY;
|
|
413
|
+
function initializeVaultDatabase(database) {
|
|
414
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
415
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
416
|
+
database.exec(`
|
|
417
|
+
CREATE TABLE IF NOT EXISTS vault (
|
|
418
|
+
user_id TEXT NOT NULL,
|
|
419
|
+
key TEXT NOT NULL,
|
|
420
|
+
value TEXT NOT NULL,
|
|
421
|
+
description TEXT NOT NULL DEFAULT '',
|
|
422
|
+
PRIMARY KEY (user_id, key)
|
|
423
|
+
)
|
|
424
|
+
`);
|
|
425
|
+
{
|
|
426
|
+
const cols = database.prepare("PRAGMA table_info(vault)").all();
|
|
427
|
+
const uid = cols.find((c) => c.name === "user_id");
|
|
428
|
+
if (uid && uid.type.toUpperCase() === "INTEGER") {
|
|
429
|
+
database.exec("BEGIN");
|
|
430
|
+
database.exec(`
|
|
431
|
+
CREATE TABLE vault_migrated (
|
|
432
|
+
user_id TEXT NOT NULL,
|
|
433
|
+
key TEXT NOT NULL,
|
|
434
|
+
value TEXT NOT NULL,
|
|
435
|
+
description TEXT NOT NULL DEFAULT '',
|
|
436
|
+
PRIMARY KEY (user_id, key)
|
|
437
|
+
)
|
|
438
|
+
`);
|
|
439
|
+
database.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
|
|
440
|
+
database.exec("DROP TABLE vault");
|
|
441
|
+
database.exec("ALTER TABLE vault_migrated RENAME TO vault");
|
|
442
|
+
database.exec("COMMIT");
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
function openVaultDatabase(dataDir) {
|
|
447
|
+
const path = join2(dataDir, "vault.db");
|
|
448
|
+
mkdirSync2(dataDir, { recursive: true });
|
|
449
|
+
const database = new Database(path, { create: true });
|
|
450
|
+
chmodSync2(path, 384);
|
|
451
|
+
initializeVaultDatabase(database);
|
|
452
|
+
return database;
|
|
453
|
+
}
|
|
454
|
+
function activeVaultDatabase() {
|
|
455
|
+
if (!vaultDb)
|
|
456
|
+
vaultDb = openVaultDatabase(DATA_DIR);
|
|
457
|
+
return vaultDb;
|
|
458
|
+
}
|
|
459
|
+
var VAULT_VALUE_MAX_BYTES = 64 * 1024;
|
|
460
|
+
function normalizeVaultKey(key) {
|
|
461
|
+
return key.trim().toUpperCase();
|
|
462
|
+
}
|
|
463
|
+
function decryptRow(userId, key, storedValue) {
|
|
464
|
+
const database = activeVaultDatabase();
|
|
465
|
+
const decoded = decryptVaultValue(userId, key, storedValue, vaultMasterKey);
|
|
466
|
+
if (decoded.legacyPlaintext) {
|
|
467
|
+
database.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value, vaultMasterKey), userId, key, storedValue);
|
|
468
|
+
}
|
|
469
|
+
return decoded.value;
|
|
470
|
+
}
|
|
471
|
+
function vaultListWithValues(userId) {
|
|
472
|
+
const rows = activeVaultDatabase().prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
473
|
+
return rows.map((row) => ({
|
|
474
|
+
key: row.key,
|
|
475
|
+
description: row.description,
|
|
476
|
+
value: decryptRow(userId, row.key, row.value)
|
|
477
|
+
}));
|
|
478
|
+
}
|
|
479
|
+
function vaultGetValue(userId, key) {
|
|
480
|
+
const normalizedKey = normalizeVaultKey(key);
|
|
481
|
+
const row = activeVaultDatabase().prepare("SELECT key, value FROM vault WHERE user_id = ? AND key = ?").get(userId, normalizedKey);
|
|
482
|
+
return row ? decryptRow(userId, row.key, row.value) : undefined;
|
|
483
|
+
}
|
|
484
|
+
function vaultSubstituteDetailed(userId, text) {
|
|
485
|
+
const entries = new Map(vaultListWithValues(userId).map((entry) => [entry.key, entry.value]));
|
|
486
|
+
const usedKeys = new Set;
|
|
487
|
+
const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey) => {
|
|
488
|
+
const key = normalizeVaultKey(rawKey);
|
|
489
|
+
const value = entries.get(key);
|
|
490
|
+
if (value === undefined)
|
|
491
|
+
return match;
|
|
492
|
+
usedKeys.add(key);
|
|
493
|
+
return value;
|
|
494
|
+
});
|
|
495
|
+
return { text: substituted, usedKeys: [...usedKeys] };
|
|
496
|
+
}
|
|
497
|
+
function encodedSecretForms(value) {
|
|
498
|
+
const forms = new Set([
|
|
499
|
+
value,
|
|
500
|
+
encodeURIComponent(value),
|
|
501
|
+
Buffer.from(value, "utf8").toString("base64"),
|
|
502
|
+
Buffer.from(value, "utf8").toString("base64url"),
|
|
503
|
+
Buffer.from(value, "utf8").toString("hex")
|
|
504
|
+
]);
|
|
505
|
+
forms.delete("");
|
|
506
|
+
return [...forms].sort((a, b) => b.length - a.length);
|
|
507
|
+
}
|
|
508
|
+
function redactVaultSecrets(userId, text) {
|
|
509
|
+
const candidates = vaultListWithValues(userId).flatMap((entry) => encodedSecretForms(entry.value).map((form) => ({ form, key: entry.key }))).sort((a, b) => b.form.length - a.form.length || a.key.localeCompare(b.key));
|
|
510
|
+
if (candidates.length === 0)
|
|
511
|
+
return text;
|
|
512
|
+
const candidatesByFirstCharacter = new Map;
|
|
513
|
+
for (const candidate of candidates) {
|
|
514
|
+
const first = candidate.form[0];
|
|
515
|
+
if (!first)
|
|
516
|
+
continue;
|
|
517
|
+
const bucket = candidatesByFirstCharacter.get(first) ?? [];
|
|
518
|
+
bucket.push(candidate);
|
|
519
|
+
candidatesByFirstCharacter.set(first, bucket);
|
|
520
|
+
}
|
|
521
|
+
let redacted = "";
|
|
522
|
+
let offset = 0;
|
|
523
|
+
while (offset < text.length) {
|
|
524
|
+
const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
|
|
525
|
+
if (!match) {
|
|
526
|
+
redacted += text[offset];
|
|
527
|
+
offset += 1;
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
redacted += `[REDACTED:${match.key}]`;
|
|
531
|
+
offset += match.form.length;
|
|
532
|
+
}
|
|
533
|
+
return redacted;
|
|
534
|
+
}
|
|
535
|
+
|
|
256
536
|
// ../../packages/core/src/agents/auth-check.ts
|
|
257
537
|
var defaultAgentAuthHost = {
|
|
258
538
|
codexAuthFilePath,
|
|
@@ -262,14 +542,20 @@ var defaultAgentAuthHost = {
|
|
|
262
542
|
operatingSystem: platform,
|
|
263
543
|
hasMacOsCredential(service) {
|
|
264
544
|
try {
|
|
265
|
-
|
|
545
|
+
execFileSync2("security", ["find-generic-password", "-s", service], { stdio: "ignore" });
|
|
266
546
|
return true;
|
|
267
547
|
} catch {
|
|
268
548
|
return false;
|
|
269
549
|
}
|
|
270
|
-
}
|
|
550
|
+
},
|
|
551
|
+
getVaultValue: vaultGetValue
|
|
271
552
|
};
|
|
272
|
-
function
|
|
553
|
+
function hasMaestroCredential(host, key, userId) {
|
|
554
|
+
if (userId && host.getVaultValue(userId, key)?.trim())
|
|
555
|
+
return true;
|
|
556
|
+
return Boolean(host.environment[key]?.trim());
|
|
557
|
+
}
|
|
558
|
+
function checkAgentAuth(agent, host = defaultAgentAuthHost, userId) {
|
|
273
559
|
switch (agent) {
|
|
274
560
|
case "codex": {
|
|
275
561
|
const path = host.codexAuthFilePath();
|
|
@@ -292,7 +578,7 @@ function checkAgentAuth(agent, host = defaultAgentAuthHost) {
|
|
|
292
578
|
error: "claude is not logged in (no macOS keychain entry 'Claude Code-credentials'). Run `claude` and complete login first"
|
|
293
579
|
};
|
|
294
580
|
}
|
|
295
|
-
const path =
|
|
581
|
+
const path = join3(host.homeDirectory(), ".claude", ".credentials.json");
|
|
296
582
|
if (host.exists(path))
|
|
297
583
|
return { ok: true };
|
|
298
584
|
return {
|
|
@@ -301,35 +587,35 @@ function checkAgentAuth(agent, host = defaultAgentAuthHost) {
|
|
|
301
587
|
};
|
|
302
588
|
}
|
|
303
589
|
case "maestro": {
|
|
304
|
-
if (host
|
|
590
|
+
if (hasMaestroCredential(host, "DEEPSEEK_API_KEY", userId) || hasMaestroCredential(host, "MOONSHOT_API_KEY", userId)) {
|
|
305
591
|
return { ok: true };
|
|
306
592
|
}
|
|
307
593
|
return {
|
|
308
594
|
ok: false,
|
|
309
|
-
error: "maestro is not authenticated (
|
|
595
|
+
error: "maestro is not authenticated (set DEEPSEEK_API_KEY or MOONSHOT_API_KEY via /vault set, or as an env var)"
|
|
310
596
|
};
|
|
311
597
|
}
|
|
312
598
|
}
|
|
313
599
|
}
|
|
314
|
-
function checkAgentModelAuth(agent, model, host = defaultAgentAuthHost) {
|
|
600
|
+
function checkAgentModelAuth(agent, model, host = defaultAgentAuthHost, userId) {
|
|
315
601
|
if (agent !== "maestro")
|
|
316
|
-
return checkAgentAuth(agent, host);
|
|
602
|
+
return checkAgentAuth(agent, host, userId);
|
|
317
603
|
if (model.startsWith("kimi")) {
|
|
318
|
-
return host
|
|
604
|
+
return hasMaestroCredential(host, "MOONSHOT_API_KEY", userId) ? { ok: true } : {
|
|
319
605
|
ok: false,
|
|
320
|
-
error: `maestro is not authenticated for model '${model}' (MOONSHOT_API_KEY env var
|
|
606
|
+
error: `maestro is not authenticated for model '${model}' (set MOONSHOT_API_KEY via /vault set, or as an env var)`
|
|
321
607
|
};
|
|
322
608
|
}
|
|
323
609
|
if (model.startsWith("deepseek")) {
|
|
324
|
-
return host
|
|
610
|
+
return hasMaestroCredential(host, "DEEPSEEK_API_KEY", userId) ? { ok: true } : {
|
|
325
611
|
ok: false,
|
|
326
|
-
error: `maestro is not authenticated for model '${model}' (DEEPSEEK_API_KEY env var
|
|
612
|
+
error: `maestro is not authenticated for model '${model}' (set DEEPSEEK_API_KEY via /vault set, or as an env var)`
|
|
327
613
|
};
|
|
328
614
|
}
|
|
329
|
-
return checkAgentAuth(agent, host);
|
|
615
|
+
return checkAgentAuth(agent, host, userId);
|
|
330
616
|
}
|
|
331
617
|
// ../../packages/core/src/agents/codex-tree-kill.ts
|
|
332
|
-
import { execFileSync as
|
|
618
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
333
619
|
|
|
334
620
|
// ../../packages/core/src/agents/codex-tree-manager.ts
|
|
335
621
|
function createCodexTreeManager(host, options = {}) {
|
|
@@ -515,7 +801,7 @@ function createCodexTreeManager(host, options = {}) {
|
|
|
515
801
|
// ../../packages/core/src/agents/codex-tree-kill.ts
|
|
516
802
|
function execText(command, args) {
|
|
517
803
|
try {
|
|
518
|
-
return
|
|
804
|
+
return execFileSync3(command, args, {
|
|
519
805
|
encoding: "utf8",
|
|
520
806
|
stdio: ["ignore", "pipe", "ignore"]
|
|
521
807
|
}).trim();
|
|
@@ -550,23 +836,23 @@ var killOwnedCodexTreesForShutdown = manager.killOwnedTreesForShutdown;
|
|
|
550
836
|
import { existsSync as existsSync7, unlinkSync as unlinkSync7 } from "fs";
|
|
551
837
|
|
|
552
838
|
// ../../packages/core/src/agents/claude-registry.ts
|
|
553
|
-
import { existsSync as existsSync4, mkdirSync as
|
|
839
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync, renameSync as renameSync2, unlinkSync as unlinkSync3 } from "fs";
|
|
554
840
|
import { homedir as homedir4 } from "os";
|
|
555
|
-
import { join as
|
|
841
|
+
import { join as join6 } from "path";
|
|
556
842
|
|
|
557
843
|
// ../../packages/core/src/agents/rollout/claude.ts
|
|
558
844
|
import { randomUUID } from "crypto";
|
|
559
845
|
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
560
846
|
import { homedir as homedir3 } from "os";
|
|
561
|
-
import { join as
|
|
847
|
+
import { join as join5 } from "path";
|
|
562
848
|
|
|
563
849
|
// ../../packages/core/src/agents/rollout/shared.ts
|
|
564
|
-
import { mkdirSync as
|
|
565
|
-
import { dirname as dirname2, join as
|
|
850
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
851
|
+
import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
|
|
566
852
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
567
853
|
var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
|
|
568
854
|
var trustedWorkspaceRoots = [WORKSPACE_DIR, DM_WORKSPACE_DIR, TOPIC_WORKSPACE_DIR].map((root) => resolve2(root));
|
|
569
|
-
var FIXTURES_DIR =
|
|
855
|
+
var FIXTURES_DIR = join4(__dirname2, "..", "fixtures");
|
|
570
856
|
function clone(obj) {
|
|
571
857
|
return structuredClone(obj);
|
|
572
858
|
}
|
|
@@ -586,7 +872,7 @@ function assertCwdInWorkspace(cwd) {
|
|
|
586
872
|
function ensureCwdExists(cwd) {
|
|
587
873
|
assertCwdInWorkspace(cwd);
|
|
588
874
|
try {
|
|
589
|
-
|
|
875
|
+
mkdirSync3(cwd, { recursive: true });
|
|
590
876
|
} catch (err) {
|
|
591
877
|
logger.warn({ err, cwd }, "rollout: ensureCwdExists failed \u2014 caller should pre-create cwd");
|
|
592
878
|
}
|
|
@@ -676,7 +962,7 @@ import {
|
|
|
676
962
|
appendFileSync,
|
|
677
963
|
closeSync,
|
|
678
964
|
fsyncSync,
|
|
679
|
-
mkdirSync as
|
|
965
|
+
mkdirSync as mkdirSync4,
|
|
680
966
|
openSync,
|
|
681
967
|
readFileSync as readFileSync2,
|
|
682
968
|
renameSync,
|
|
@@ -739,7 +1025,7 @@ function isStaleLock(lockPath) {
|
|
|
739
1025
|
}
|
|
740
1026
|
}
|
|
741
1027
|
function appendJsonlLine(filePath, line) {
|
|
742
|
-
|
|
1028
|
+
mkdirSync4(dirname3(filePath), { recursive: true });
|
|
743
1029
|
const lockPath = `${filePath}${LOCK_SUFFIX}`;
|
|
744
1030
|
const payload = line.endsWith(`
|
|
745
1031
|
`) ? line : `${line}
|
|
@@ -772,7 +1058,7 @@ function appendJsonlLine(filePath, line) {
|
|
|
772
1058
|
}
|
|
773
1059
|
function writeJsonlFile(filePath, entries) {
|
|
774
1060
|
const dir = dirname3(filePath);
|
|
775
|
-
|
|
1061
|
+
mkdirSync4(dir, { recursive: true });
|
|
776
1062
|
const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
777
1063
|
const payload = `${entries.map((e) => JSON.stringify(e)).join(`
|
|
778
1064
|
`)}
|
|
@@ -817,7 +1103,7 @@ var _attachmentsCache = null;
|
|
|
817
1103
|
function loadClaudeAttachments() {
|
|
818
1104
|
if (_attachmentsCache)
|
|
819
1105
|
return _attachmentsCache;
|
|
820
|
-
const raw = readFileSync3(
|
|
1106
|
+
const raw = readFileSync3(join5(FIXTURES_DIR, "claude-attachments.jsonl"), "utf8");
|
|
821
1107
|
const lines = parseJsonlText(raw);
|
|
822
1108
|
if (lines.length < 2) {
|
|
823
1109
|
throw new Error(`loadClaudeAttachments: expected >=2 entries in claude-attachments.jsonl, got ${lines.length}`);
|
|
@@ -921,8 +1207,8 @@ function writeClaudeRollout(opts) {
|
|
|
921
1207
|
});
|
|
922
1208
|
lastUuid = assistantUuid;
|
|
923
1209
|
}
|
|
924
|
-
const projectsDir =
|
|
925
|
-
const path =
|
|
1210
|
+
const projectsDir = join5(homedir3(), ".claude", "projects", encodeClaudeCwd(opts.cwd));
|
|
1211
|
+
const path = join5(projectsDir, `${sessionId}.jsonl`);
|
|
926
1212
|
writeJsonlFile(path, lines);
|
|
927
1213
|
logger.info({ sessionId, path, pairs: pairs.length }, "writeClaudeRollout: synthetic rollout placed");
|
|
928
1214
|
return { sessionId, rolloutPath: path };
|
|
@@ -967,15 +1253,15 @@ var claudeRegistry = {
|
|
|
967
1253
|
const result = await forkSession(parentSessionId, {
|
|
968
1254
|
...title ? { title } : {}
|
|
969
1255
|
});
|
|
970
|
-
const projectsRoot =
|
|
971
|
-
const destDir =
|
|
972
|
-
const destPath =
|
|
1256
|
+
const projectsRoot = join6(homedir4(), ".claude", "projects");
|
|
1257
|
+
const destDir = join6(projectsRoot, encodeClaudeCwd(cwd));
|
|
1258
|
+
const destPath = join6(destDir, `${result.sessionId}.jsonl`);
|
|
973
1259
|
if (!existsSync4(destPath)) {
|
|
974
|
-
const sourcePath = readdirSync(projectsRoot).map((d) =>
|
|
1260
|
+
const sourcePath = readdirSync(projectsRoot).map((d) => join6(projectsRoot, d, `${result.sessionId}.jsonl`)).find((p) => existsSync4(p));
|
|
975
1261
|
if (!sourcePath) {
|
|
976
1262
|
throw new Error(`claude forkSession: fork rollout ${result.sessionId}.jsonl not found under ${projectsRoot}`);
|
|
977
1263
|
}
|
|
978
|
-
|
|
1264
|
+
mkdirSync5(destDir, { recursive: true });
|
|
979
1265
|
renameSync2(sourcePath, destPath);
|
|
980
1266
|
}
|
|
981
1267
|
return {
|
|
@@ -984,10 +1270,10 @@ var claudeRegistry = {
|
|
|
984
1270
|
};
|
|
985
1271
|
},
|
|
986
1272
|
async cleanupRollouts({ cwd, sessionIds }) {
|
|
987
|
-
const projectsDir =
|
|
1273
|
+
const projectsDir = join6(homedir4(), ".claude", "projects", encodeClaudeCwd(cwd));
|
|
988
1274
|
const failures = [];
|
|
989
1275
|
for (const sid of sessionIds) {
|
|
990
|
-
const path =
|
|
1276
|
+
const path = join6(projectsDir, `${sid}.jsonl`);
|
|
991
1277
|
try {
|
|
992
1278
|
unlinkSync3(path);
|
|
993
1279
|
} catch (e) {
|
|
@@ -1006,21 +1292,21 @@ var claudeRegistry = {
|
|
|
1006
1292
|
// ../../packages/core/src/agents/codex-registry.ts
|
|
1007
1293
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
1008
1294
|
import { homedir as homedir7 } from "os";
|
|
1009
|
-
import { join as
|
|
1295
|
+
import { join as join10 } from "path";
|
|
1010
1296
|
|
|
1011
1297
|
// ../../packages/core/src/agents/rollout/codex.ts
|
|
1012
|
-
import { randomBytes as
|
|
1013
|
-
import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
1298
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
1299
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, realpathSync, statSync as statSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
1014
1300
|
import { homedir as homedir5 } from "os";
|
|
1015
|
-
import { join as
|
|
1301
|
+
import { basename, dirname as dirname4, join as join7, resolve as resolve3 } from "path";
|
|
1016
1302
|
function codexSessionsDir() {
|
|
1017
|
-
return
|
|
1303
|
+
return join7(process.env.CODEX_HOME || join7(homedir5(), ".codex"), "sessions");
|
|
1018
1304
|
}
|
|
1019
1305
|
var _shellCache = null;
|
|
1020
1306
|
function loadCodexShell() {
|
|
1021
1307
|
if (_shellCache)
|
|
1022
1308
|
return _shellCache;
|
|
1023
|
-
const raw = readFileSync4(
|
|
1309
|
+
const raw = readFileSync4(join7(FIXTURES_DIR, "codex-shell.jsonl"), "utf8");
|
|
1024
1310
|
const lines = parseJsonlText(raw);
|
|
1025
1311
|
if (lines.length < 5) {
|
|
1026
1312
|
throw new Error(`loadCodexShell: expected >=5 entries in codex-shell.jsonl, got ${lines.length}`);
|
|
@@ -1037,7 +1323,7 @@ function loadCodexShell() {
|
|
|
1037
1323
|
function uuidv7() {
|
|
1038
1324
|
const ts = Date.now();
|
|
1039
1325
|
const tsHex = ts.toString(16).padStart(12, "0");
|
|
1040
|
-
const rand =
|
|
1326
|
+
const rand = randomBytes3(10);
|
|
1041
1327
|
rand[0] = rand[0] & 15 | 112;
|
|
1042
1328
|
rand[2] = rand[2] & 63 | 128;
|
|
1043
1329
|
return [
|
|
@@ -1078,6 +1364,139 @@ function patchEnvContext(envContext, cwd, currentDate, timezone) {
|
|
|
1078
1364
|
}
|
|
1079
1365
|
}
|
|
1080
1366
|
}
|
|
1367
|
+
function canonicalFilePath(path) {
|
|
1368
|
+
const absolute = resolve3(path);
|
|
1369
|
+
try {
|
|
1370
|
+
return realpathSync(absolute);
|
|
1371
|
+
} catch {
|
|
1372
|
+
try {
|
|
1373
|
+
return join7(realpathSync(dirname4(absolute)), basename(absolute));
|
|
1374
|
+
} catch {
|
|
1375
|
+
return absolute;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
function changedPatchLines(value) {
|
|
1380
|
+
const removed = [];
|
|
1381
|
+
const added = [];
|
|
1382
|
+
const diffPreview = [];
|
|
1383
|
+
let oldLine = 0;
|
|
1384
|
+
let newLine = 0;
|
|
1385
|
+
let sawHunk = false;
|
|
1386
|
+
for (const line of value.split(`
|
|
1387
|
+
`)) {
|
|
1388
|
+
const hunk = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
|
|
1389
|
+
if (hunk) {
|
|
1390
|
+
if (sawHunk)
|
|
1391
|
+
diffPreview.push("\u2026");
|
|
1392
|
+
sawHunk = true;
|
|
1393
|
+
oldLine = Number.parseInt(hunk[1] ?? "0", 10);
|
|
1394
|
+
newLine = Number.parseInt(hunk[2] ?? "0", 10);
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
if (!sawHunk && (line.startsWith("--- ") || line.startsWith("+++ ")))
|
|
1398
|
+
continue;
|
|
1399
|
+
if (line.startsWith("\\ No newline"))
|
|
1400
|
+
continue;
|
|
1401
|
+
if (line.startsWith("-")) {
|
|
1402
|
+
removed.push(line.slice(1));
|
|
1403
|
+
diffPreview.push(`${oldLine} -${line.slice(1)}`);
|
|
1404
|
+
oldLine += 1;
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
if (line.startsWith("+")) {
|
|
1408
|
+
added.push(line.slice(1));
|
|
1409
|
+
diffPreview.push(`${newLine} +${line.slice(1)}`);
|
|
1410
|
+
newLine += 1;
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
if (line.startsWith(" ")) {
|
|
1414
|
+
diffPreview.push(`${newLine} ${line.slice(1)}`);
|
|
1415
|
+
oldLine += 1;
|
|
1416
|
+
newLine += 1;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return {
|
|
1420
|
+
...removed.length > 0 ? { before: removed.join(`
|
|
1421
|
+
`) } : {},
|
|
1422
|
+
...added.length > 0 ? { after: added.join(`
|
|
1423
|
+
`) } : {},
|
|
1424
|
+
...diffPreview.length > 0 ? { diffPreview: diffPreview.join(`
|
|
1425
|
+
`) } : {}
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
function extractLatestCodexPatchPreview(jsonl, expectedPaths, consumedCallIds = new Set, expectedCallId) {
|
|
1429
|
+
const expected = expectedPaths.map(canonicalFilePath);
|
|
1430
|
+
const lines = jsonl.trimEnd().split(`
|
|
1431
|
+
`);
|
|
1432
|
+
for (let index = lines.length - 1;index >= 0; index -= 1) {
|
|
1433
|
+
try {
|
|
1434
|
+
const entry = JSON.parse(lines[index] ?? "");
|
|
1435
|
+
if (entry.type !== "event_msg" || entry.payload?.type !== "patch_apply_end")
|
|
1436
|
+
continue;
|
|
1437
|
+
const callId = entry.payload.call_id;
|
|
1438
|
+
const rawChanges = entry.payload.changes;
|
|
1439
|
+
if (!callId || consumedCallIds.has(callId) || expectedCallId !== undefined && callId !== expectedCallId || !rawChanges) {
|
|
1440
|
+
continue;
|
|
1441
|
+
}
|
|
1442
|
+
const entries = Object.entries(rawChanges);
|
|
1443
|
+
const matched = expected.map((path) => entries.find(([candidate]) => canonicalFilePath(candidate) === path));
|
|
1444
|
+
const complete = matched.filter((change) => change !== undefined);
|
|
1445
|
+
if (complete.length !== expected.length)
|
|
1446
|
+
continue;
|
|
1447
|
+
return {
|
|
1448
|
+
callId,
|
|
1449
|
+
changes: complete.map(([path, change]) => {
|
|
1450
|
+
if (typeof change.unified_diff === "string") {
|
|
1451
|
+
return { path, ...changedPatchLines(change.unified_diff) };
|
|
1452
|
+
}
|
|
1453
|
+
const content = typeof change.content === "string" ? change.content.replace(/\n$/, "") : undefined;
|
|
1454
|
+
return {
|
|
1455
|
+
path,
|
|
1456
|
+
...change.type === "delete" && content !== undefined ? { before: content } : {},
|
|
1457
|
+
...change.type === "add" && content !== undefined ? { after: content } : {}
|
|
1458
|
+
};
|
|
1459
|
+
})
|
|
1460
|
+
};
|
|
1461
|
+
} catch {}
|
|
1462
|
+
}
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
function extractCodexPatchCallIds(jsonl) {
|
|
1466
|
+
const callIds = [];
|
|
1467
|
+
for (const line of jsonl.trimEnd().split(`
|
|
1468
|
+
`)) {
|
|
1469
|
+
try {
|
|
1470
|
+
const entry = JSON.parse(line);
|
|
1471
|
+
if (entry.type === "event_msg" && entry.payload?.type === "patch_apply_end" && typeof entry.payload.call_id === "string") {
|
|
1472
|
+
callIds.push(entry.payload.call_id);
|
|
1473
|
+
}
|
|
1474
|
+
} catch {}
|
|
1475
|
+
}
|
|
1476
|
+
return callIds;
|
|
1477
|
+
}
|
|
1478
|
+
function readCodexPatchCallIds(threadId) {
|
|
1479
|
+
const path = latestCodexRolloutPath(threadId);
|
|
1480
|
+
if (!path)
|
|
1481
|
+
return [];
|
|
1482
|
+
try {
|
|
1483
|
+
return extractCodexPatchCallIds(readFileSync4(path, "utf8"));
|
|
1484
|
+
} catch (error) {
|
|
1485
|
+
logger.debug({ error, threadId }, "codex patch ids: rollout read failed");
|
|
1486
|
+
return [];
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
function readLatestCodexPatchPreview(threadId, expectedPaths, consumedCallIds = new Set, expectedCallId) {
|
|
1490
|
+
const path = latestCodexRolloutPath(threadId);
|
|
1491
|
+
if (!path)
|
|
1492
|
+
return;
|
|
1493
|
+
try {
|
|
1494
|
+
return extractLatestCodexPatchPreview(readFileSync4(path, "utf8"), expectedPaths, consumedCallIds, expectedCallId);
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
logger.debug({ error, threadId }, "codex patch preview: rollout read failed");
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1081
1500
|
function extractLatestCodexContextUsage(jsonl) {
|
|
1082
1501
|
const lines = jsonl.trimEnd().split(`
|
|
1083
1502
|
`);
|
|
@@ -1102,18 +1521,18 @@ function readLatestCodexContextUsage(threadId) {
|
|
|
1102
1521
|
try {
|
|
1103
1522
|
if (buckets) {
|
|
1104
1523
|
for (const bucket of buckets) {
|
|
1105
|
-
const dir =
|
|
1524
|
+
const dir = join7(sessionsDir, bucket);
|
|
1106
1525
|
if (!existsSync5(dir))
|
|
1107
1526
|
continue;
|
|
1108
1527
|
const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
|
|
1109
1528
|
for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
|
|
1110
|
-
candidates.push(
|
|
1529
|
+
candidates.push(join7(dir, rel));
|
|
1111
1530
|
}
|
|
1112
1531
|
}
|
|
1113
1532
|
} else {
|
|
1114
1533
|
const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
|
|
1115
1534
|
for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
|
|
1116
|
-
candidates.push(
|
|
1535
|
+
candidates.push(join7(sessionsDir, rel));
|
|
1117
1536
|
}
|
|
1118
1537
|
}
|
|
1119
1538
|
const path = candidates.sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs)[0];
|
|
@@ -1157,19 +1576,19 @@ function latestCodexRolloutPath(threadId) {
|
|
|
1157
1576
|
try {
|
|
1158
1577
|
if (buckets) {
|
|
1159
1578
|
for (const bucket of buckets) {
|
|
1160
|
-
const dir =
|
|
1579
|
+
const dir = join7(sessionsDir, bucket);
|
|
1161
1580
|
if (!existsSync5(dir))
|
|
1162
1581
|
continue;
|
|
1163
1582
|
const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
|
|
1164
1583
|
for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
|
|
1165
|
-
candidates.push(
|
|
1584
|
+
candidates.push(join7(dir, rel));
|
|
1166
1585
|
}
|
|
1167
1586
|
}
|
|
1168
1587
|
}
|
|
1169
1588
|
if (candidates.length === 0) {
|
|
1170
1589
|
const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
|
|
1171
1590
|
for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
|
|
1172
|
-
candidates.push(
|
|
1591
|
+
candidates.push(join7(sessionsDir, rel));
|
|
1173
1592
|
}
|
|
1174
1593
|
}
|
|
1175
1594
|
return candidates.sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs)[0];
|
|
@@ -1278,8 +1697,8 @@ function writeCodexRollout(opts) {
|
|
|
1278
1697
|
const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
1279
1698
|
const dd = String(now.getUTCDate()).padStart(2, "0");
|
|
1280
1699
|
const tsStr = tsIso.replace(/[:.]/g, "-").slice(0, 19);
|
|
1281
|
-
const dir =
|
|
1282
|
-
const path =
|
|
1700
|
+
const dir = join7(codexSessionsDir(), String(yyyy), mm, dd);
|
|
1701
|
+
const path = join7(dir, `rollout-${tsStr}-${threadId}.jsonl`);
|
|
1283
1702
|
if (opts.threadId) {
|
|
1284
1703
|
sweepPriorRolloutsForThread(opts.threadId);
|
|
1285
1704
|
}
|
|
@@ -1295,13 +1714,13 @@ function sweepPriorRolloutsForThread(threadId) {
|
|
|
1295
1714
|
return;
|
|
1296
1715
|
}
|
|
1297
1716
|
for (const bucket of buckets) {
|
|
1298
|
-
const dir =
|
|
1717
|
+
const dir = join7(sessionsDir, bucket);
|
|
1299
1718
|
if (!existsSync5(dir))
|
|
1300
1719
|
continue;
|
|
1301
1720
|
try {
|
|
1302
1721
|
const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
|
|
1303
1722
|
for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
|
|
1304
|
-
const fullPath =
|
|
1723
|
+
const fullPath = join7(dir, rel);
|
|
1305
1724
|
try {
|
|
1306
1725
|
unlinkSync4(fullPath);
|
|
1307
1726
|
} catch (e) {
|
|
@@ -1319,7 +1738,7 @@ function sweepPriorRolloutsFullTree(threadId, sessionsDir) {
|
|
|
1319
1738
|
try {
|
|
1320
1739
|
const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
|
|
1321
1740
|
for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
|
|
1322
|
-
const fullPath =
|
|
1741
|
+
const fullPath = join7(sessionsDir, rel);
|
|
1323
1742
|
try {
|
|
1324
1743
|
unlinkSync4(fullPath);
|
|
1325
1744
|
} catch (e) {
|
|
@@ -1379,13 +1798,13 @@ function formatDateBucket(d) {
|
|
|
1379
1798
|
// ../../packages/core/src/storage/conversations.ts
|
|
1380
1799
|
import {
|
|
1381
1800
|
existsSync as existsSync6,
|
|
1382
|
-
mkdirSync as
|
|
1801
|
+
mkdirSync as mkdirSync7,
|
|
1383
1802
|
readFileSync as readFileSync5,
|
|
1384
1803
|
renameSync as renameSync3,
|
|
1385
1804
|
unlinkSync as unlinkSync5,
|
|
1386
1805
|
writeFileSync as writeFileSync4
|
|
1387
1806
|
} from "fs";
|
|
1388
|
-
import { dirname as
|
|
1807
|
+
import { dirname as dirname6, join as join9 } from "path";
|
|
1389
1808
|
|
|
1390
1809
|
// ../../packages/core/src/security/sanitize.ts
|
|
1391
1810
|
function sanitizeTopicName(name, lowercase = false) {
|
|
@@ -1400,62 +1819,9 @@ function sanitizeFileName(name) {
|
|
|
1400
1819
|
}
|
|
1401
1820
|
|
|
1402
1821
|
// ../../packages/core/src/storage/storage-host.ts
|
|
1403
|
-
import { mkdirSync as
|
|
1822
|
+
import { mkdirSync as mkdirSync6 } from "fs";
|
|
1404
1823
|
import { homedir as homedir6 } from "os";
|
|
1405
|
-
import { dirname as
|
|
1406
|
-
|
|
1407
|
-
// ../../packages/core/src/storage/sqlite.ts
|
|
1408
|
-
var isBun = typeof process.versions.bun === "string";
|
|
1409
|
-
var Database;
|
|
1410
|
-
if (isBun) {
|
|
1411
|
-
({ Database } = await import("bun:sqlite"));
|
|
1412
|
-
} else {
|
|
1413
|
-
const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
|
|
1414
|
-
const { DatabaseSync } = await import(nodeSqliteSpecifier);
|
|
1415
|
-
|
|
1416
|
-
class NodeDatabase {
|
|
1417
|
-
#db;
|
|
1418
|
-
constructor(path, options = {}) {
|
|
1419
|
-
this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
|
|
1420
|
-
}
|
|
1421
|
-
query(sql) {
|
|
1422
|
-
return this.#db.prepare(sql);
|
|
1423
|
-
}
|
|
1424
|
-
prepare(sql) {
|
|
1425
|
-
return this.#db.prepare(sql);
|
|
1426
|
-
}
|
|
1427
|
-
exec(sql) {
|
|
1428
|
-
this.#db.exec(sql);
|
|
1429
|
-
}
|
|
1430
|
-
run(sql, ...params) {
|
|
1431
|
-
return this.#db.prepare(sql).run(...params);
|
|
1432
|
-
}
|
|
1433
|
-
transaction(fn) {
|
|
1434
|
-
const run = (begin) => (...args) => {
|
|
1435
|
-
this.#db.exec(begin);
|
|
1436
|
-
try {
|
|
1437
|
-
const result = fn(...args);
|
|
1438
|
-
this.#db.exec("COMMIT");
|
|
1439
|
-
return result;
|
|
1440
|
-
} catch (err) {
|
|
1441
|
-
this.#db.exec("ROLLBACK");
|
|
1442
|
-
throw err;
|
|
1443
|
-
}
|
|
1444
|
-
};
|
|
1445
|
-
const tx = run("BEGIN");
|
|
1446
|
-
tx.deferred = run("BEGIN DEFERRED");
|
|
1447
|
-
tx.immediate = run("BEGIN IMMEDIATE");
|
|
1448
|
-
tx.exclusive = run("BEGIN EXCLUSIVE");
|
|
1449
|
-
return tx;
|
|
1450
|
-
}
|
|
1451
|
-
close() {
|
|
1452
|
-
this.#db.close();
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
Database = NodeDatabase;
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
// ../../packages/core/src/storage/storage-host.ts
|
|
1824
|
+
import { dirname as dirname5, join as join8, resolve as resolve4 } from "path";
|
|
1459
1825
|
var configuredHost = {};
|
|
1460
1826
|
var fallbackDatabase = null;
|
|
1461
1827
|
var fallbackDatabasePath = null;
|
|
@@ -1464,19 +1830,19 @@ var initializedSchemas = new WeakMap;
|
|
|
1464
1830
|
var initializingDatabases = new WeakSet;
|
|
1465
1831
|
function envPath(name, fallback) {
|
|
1466
1832
|
const value = process.env[name]?.trim();
|
|
1467
|
-
return
|
|
1833
|
+
return resolve4(value || fallback);
|
|
1468
1834
|
}
|
|
1469
1835
|
function defaultStateDir() {
|
|
1470
|
-
return envPath("NEGOTIUM_STATE_DIR",
|
|
1836
|
+
return envPath("NEGOTIUM_STATE_DIR", join8(homedir6(), ".negotium"));
|
|
1471
1837
|
}
|
|
1472
1838
|
function defaultDataDir() {
|
|
1473
|
-
return envPath("NEGOTIUM_DATA_DIR",
|
|
1839
|
+
return envPath("NEGOTIUM_DATA_DIR", join8(defaultStateDir(), "data"));
|
|
1474
1840
|
}
|
|
1475
1841
|
function defaultWorkspaceDir() {
|
|
1476
|
-
return envPath("NEGOTIUM_WORKSPACE_DIR",
|
|
1842
|
+
return envPath("NEGOTIUM_WORKSPACE_DIR", join8(defaultStateDir(), "workspace"));
|
|
1477
1843
|
}
|
|
1478
1844
|
function defaultSessionsDatabasePath() {
|
|
1479
|
-
return envPath("SESSIONS_DB_PATH",
|
|
1845
|
+
return envPath("SESSIONS_DB_PATH", join8(resolveStorageDataDir(), "sessions.db"));
|
|
1480
1846
|
}
|
|
1481
1847
|
function initializeDatabase(database) {
|
|
1482
1848
|
database.exec("PRAGMA journal_mode = WAL");
|
|
@@ -1493,7 +1859,7 @@ function defaultDatabase() {
|
|
|
1493
1859
|
return fallbackDatabase;
|
|
1494
1860
|
if (fallbackDatabase)
|
|
1495
1861
|
fallbackDatabase.close();
|
|
1496
|
-
|
|
1862
|
+
mkdirSync6(dirname5(path), { recursive: true });
|
|
1497
1863
|
fallbackDatabase = new Database(path, { create: true });
|
|
1498
1864
|
fallbackDatabasePath = path;
|
|
1499
1865
|
initializeDatabase(fallbackDatabase);
|
|
@@ -1509,7 +1875,7 @@ function resolveStorageWorkspaceDir() {
|
|
|
1509
1875
|
return configuredHost.workspaceDir ?? defaultWorkspaceDir();
|
|
1510
1876
|
}
|
|
1511
1877
|
function resolveStorageSharedWikiDir() {
|
|
1512
|
-
return configuredHost.sharedWikiDir ??
|
|
1878
|
+
return configuredHost.sharedWikiDir ?? join8(resolveStorageWorkspaceDir(), "wiki");
|
|
1513
1879
|
}
|
|
1514
1880
|
function registerStorageSchemaInitializer(initialize, priority = 100) {
|
|
1515
1881
|
schemaInitializers.push({ initialize, priority });
|
|
@@ -1563,14 +1929,14 @@ function safeUserIdComponent(userId) {
|
|
|
1563
1929
|
return str;
|
|
1564
1930
|
}
|
|
1565
1931
|
function conversationDir(userId) {
|
|
1566
|
-
return
|
|
1932
|
+
return join9(resolveStorageDataDir(), "conversations", safeUserIdComponent(userId));
|
|
1567
1933
|
}
|
|
1568
1934
|
function topicFilename(topicName) {
|
|
1569
1935
|
const t = sanitizeTopicName(topicName, true);
|
|
1570
1936
|
return `${t}.jsonl`;
|
|
1571
1937
|
}
|
|
1572
1938
|
function getConversationPath(userId, topicName) {
|
|
1573
|
-
return
|
|
1939
|
+
return join9(conversationDir(userId), topicFilename(topicName));
|
|
1574
1940
|
}
|
|
1575
1941
|
function appendConversationEvent(userId, topicName, agent, event) {
|
|
1576
1942
|
try {
|
|
@@ -1586,7 +1952,7 @@ function appendConversationEventStrict(userId, topicName, agent, event) {
|
|
|
1586
1952
|
agent,
|
|
1587
1953
|
event
|
|
1588
1954
|
};
|
|
1589
|
-
|
|
1955
|
+
mkdirSync7(dirname6(path), { recursive: true });
|
|
1590
1956
|
appendJsonlLine(path, JSON.stringify(entry));
|
|
1591
1957
|
}
|
|
1592
1958
|
function readConversation(userId, topicName) {
|
|
@@ -1655,13 +2021,13 @@ var codexRegistry = {
|
|
|
1655
2021
|
async cleanupRollouts({ sessionIds }) {
|
|
1656
2022
|
if (sessionIds.length === 0)
|
|
1657
2023
|
return;
|
|
1658
|
-
const sessionsDir =
|
|
2024
|
+
const sessionsDir = join10(process.env.CODEX_HOME || join10(homedir7(), ".codex"), "sessions");
|
|
1659
2025
|
const failures = [];
|
|
1660
2026
|
for (const tid of sessionIds) {
|
|
1661
2027
|
try {
|
|
1662
2028
|
const glob = new Bun.Glob(`**/rollout-*-${tid}.jsonl`);
|
|
1663
2029
|
for await (const rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
|
|
1664
|
-
const path =
|
|
2030
|
+
const path = join10(sessionsDir, rel);
|
|
1665
2031
|
try {
|
|
1666
2032
|
unlinkSync6(path);
|
|
1667
2033
|
} catch (e) {
|
|
@@ -1747,8 +2113,8 @@ function cleanupAgentFork(handle) {
|
|
|
1747
2113
|
defaultForkHelpers.cleanupAgentFork(handle);
|
|
1748
2114
|
}
|
|
1749
2115
|
// ../../packages/core/src/agents/archiver.ts
|
|
1750
|
-
import { randomUUID as
|
|
1751
|
-
import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as
|
|
2116
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
2117
|
+
import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync11, statSync as statSync5 } from "fs";
|
|
1752
2118
|
import { join as join15 } from "path";
|
|
1753
2119
|
|
|
1754
2120
|
// ../../packages/core/src/agents/index.ts
|
|
@@ -1781,8 +2147,8 @@ function deepMapStrings(value, fn) {
|
|
|
1781
2147
|
import { AsyncLocalStorage } from "async_hooks";
|
|
1782
2148
|
|
|
1783
2149
|
// ../../packages/core/src/security/sensitive-path.ts
|
|
1784
|
-
import { realpathSync } from "fs";
|
|
1785
|
-
import { resolve as
|
|
2150
|
+
import { realpathSync as realpathSync2 } from "fs";
|
|
2151
|
+
import { resolve as resolve5 } from "path";
|
|
1786
2152
|
var SENSITIVE_PATH_PATTERNS = [
|
|
1787
2153
|
/\/\.env(\.|$)/i,
|
|
1788
2154
|
/\/\.ssh\//i,
|
|
@@ -1799,11 +2165,11 @@ var SENSITIVE_PATH_PATTERNS = [
|
|
|
1799
2165
|
/\/sessions\.db(-wal|-shm|-journal)?$/i
|
|
1800
2166
|
];
|
|
1801
2167
|
function isSensitivePath(filePath) {
|
|
1802
|
-
const normalized =
|
|
2168
|
+
const normalized = resolve5(filePath);
|
|
1803
2169
|
if (SENSITIVE_PATH_PATTERNS.some((p) => p.test(normalized)))
|
|
1804
2170
|
return true;
|
|
1805
2171
|
try {
|
|
1806
|
-
const real =
|
|
2172
|
+
const real = realpathSync2(normalized);
|
|
1807
2173
|
if (real !== normalized)
|
|
1808
2174
|
return SENSITIVE_PATH_PATTERNS.some((p) => p.test(real));
|
|
1809
2175
|
} catch {}
|
|
@@ -1932,8 +2298,8 @@ function peerSessionBridgeIpcEnv() {
|
|
|
1932
2298
|
}
|
|
1933
2299
|
|
|
1934
2300
|
// ../../packages/core/src/platform/background-bash/manager.ts
|
|
1935
|
-
import { execFileSync as
|
|
1936
|
-
import { randomBytes as
|
|
2301
|
+
import { execFileSync as execFileSync4, spawn } from "child_process";
|
|
2302
|
+
import { randomBytes as randomBytes4 } from "crypto";
|
|
1937
2303
|
|
|
1938
2304
|
// ../../packages/core/src/platform/delay.ts
|
|
1939
2305
|
var delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
@@ -1950,7 +2316,7 @@ function makeBgBashKey(_userId, _topic) {
|
|
|
1950
2316
|
}
|
|
1951
2317
|
function defaultPortPids(port) {
|
|
1952
2318
|
try {
|
|
1953
|
-
return
|
|
2319
|
+
return execFileSync4("lsof", ["-i", `:${port}`, "-t"], { stdio: "pipe" }).toString().trim().split(`
|
|
1954
2320
|
`).map((pid) => Number.parseInt(pid, 10)).filter((pid) => !Number.isNaN(pid));
|
|
1955
2321
|
} catch {
|
|
1956
2322
|
return [];
|
|
@@ -1961,8 +2327,8 @@ function createBackgroundBashManager(options = {}) {
|
|
|
1961
2327
|
const usedPorts = new Set;
|
|
1962
2328
|
const spawning = new Map;
|
|
1963
2329
|
const knownContexts = new Map;
|
|
1964
|
-
const runtimeCapability = options.capability ??
|
|
1965
|
-
const runtimeServerId = options.serverId ??
|
|
2330
|
+
const runtimeCapability = options.capability ?? randomBytes4(32).toString("hex");
|
|
2331
|
+
const runtimeServerId = options.serverId ?? randomBytes4(16).toString("hex");
|
|
1966
2332
|
const serverFile = options.serverFile ?? BACKGROUND_BASH_SERVER;
|
|
1967
2333
|
const basePort = options.basePort ?? BG_BASH_BASE_PORT;
|
|
1968
2334
|
const maxPort = options.maxPort ?? BG_BASH_MAX_PORT;
|
|
@@ -2107,12 +2473,12 @@ function createBackgroundBashManager(options = {}) {
|
|
|
2107
2473
|
usedPorts.clear();
|
|
2108
2474
|
knownContexts.clear();
|
|
2109
2475
|
const deadline = now() + 3000;
|
|
2110
|
-
await Promise.all(entries.map((instance) => new Promise((
|
|
2476
|
+
await Promise.all(entries.map((instance) => new Promise((resolve6) => {
|
|
2111
2477
|
if (instance.process.exitCode !== null || instance.process.killed)
|
|
2112
|
-
return
|
|
2113
|
-
instance.process.once("exit",
|
|
2114
|
-
instance.process.once("error",
|
|
2115
|
-
const timer = setTimeout(
|
|
2478
|
+
return resolve6();
|
|
2479
|
+
instance.process.once("exit", resolve6);
|
|
2480
|
+
instance.process.once("error", resolve6);
|
|
2481
|
+
const timer = setTimeout(resolve6, Math.max(0, deadline - now()));
|
|
2116
2482
|
timer.unref?.();
|
|
2117
2483
|
})));
|
|
2118
2484
|
}
|
|
@@ -2201,23 +2567,23 @@ function browserOwnerForContext(ctx) {
|
|
|
2201
2567
|
function playwrightTransport(port, owner, capability, agent) {
|
|
2202
2568
|
const ownerCapability = browserOwnerCapability(capability, owner);
|
|
2203
2569
|
if (agent === "codex") {
|
|
2570
|
+
const query2 = new URLSearchParams({ owner });
|
|
2204
2571
|
return {
|
|
2205
|
-
url: `http://127.0.0.1:${port}/mcp`,
|
|
2206
|
-
http_headers: { "X-Browser-Owner": owner },
|
|
2572
|
+
url: `http://127.0.0.1:${port}/mcp?${query2}`,
|
|
2207
2573
|
env_http_headers: { "X-Browser-Capability": CODEX_BROWSER_CAPABILITY_ENV }
|
|
2208
2574
|
};
|
|
2209
2575
|
}
|
|
2576
|
+
const query = new URLSearchParams({ owner });
|
|
2210
2577
|
if (agent === "maestro") {
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
};
|
|
2578
|
+
return buildStdioMcpServer("maestro", BROWSER_MCP_SSE_PROXY_SERVER, [], {
|
|
2579
|
+
NEGOTIUM_BROWSER_SSE_URL: `http://127.0.0.1:${port}/sse?${query}`,
|
|
2580
|
+
NEGOTIUM_BROWSER_OWNER_CAPABILITY: ownerCapability
|
|
2581
|
+
});
|
|
2216
2582
|
}
|
|
2217
2583
|
return {
|
|
2218
2584
|
type: "sse",
|
|
2219
|
-
url: `http://127.0.0.1:${port}/sse`,
|
|
2220
|
-
headers: { "X-Browser-
|
|
2585
|
+
url: `http://127.0.0.1:${port}/sse?${query}`,
|
|
2586
|
+
headers: { "X-Browser-Capability": ownerCapability }
|
|
2221
2587
|
};
|
|
2222
2588
|
}
|
|
2223
2589
|
function longLivedHttpMcp(agent, port) {
|
|
@@ -2495,288 +2861,119 @@ function getForumMcpServers(opts) {
|
|
|
2495
2861
|
} = opts;
|
|
2496
2862
|
const filter = (name) => {
|
|
2497
2863
|
if (silent && name === "task")
|
|
2498
|
-
return false;
|
|
2499
|
-
if (enabled === null)
|
|
2500
|
-
return true;
|
|
2501
|
-
return enabled.includes(name) || REQUIRED_FORUM_MCP_SERVERS.includes(name);
|
|
2502
|
-
};
|
|
2503
|
-
const base = buildScope("forum", {
|
|
2504
|
-
userId,
|
|
2505
|
-
session,
|
|
2506
|
-
topicId,
|
|
2507
|
-
queryId,
|
|
2508
|
-
wikiTopicId,
|
|
2509
|
-
agent,
|
|
2510
|
-
cwd,
|
|
2511
|
-
model,
|
|
2512
|
-
currentUserPrompt,
|
|
2513
|
-
depth,
|
|
2514
|
-
playwrightPort,
|
|
2515
|
-
playwrightCapability,
|
|
2516
|
-
bgBashPort,
|
|
2517
|
-
autoContinue,
|
|
2518
|
-
visualTools,
|
|
2519
|
-
fileDeliveryTools,
|
|
2520
|
-
silent,
|
|
2521
|
-
peerBridge
|
|
2522
|
-
}, filter);
|
|
2523
|
-
return { ...base, ...extra };
|
|
2524
|
-
}
|
|
2525
|
-
function getCronMcpServers(opts) {
|
|
2526
|
-
return buildScope("cron", {
|
|
2527
|
-
userId: opts.userId,
|
|
2528
|
-
session: opts.session,
|
|
2529
|
-
topicId: opts.topicId,
|
|
2530
|
-
queryId: opts.queryId,
|
|
2531
|
-
wikiTopicId: opts.wikiTopicId ?? opts.topicId,
|
|
2532
|
-
agent: opts.agent,
|
|
2533
|
-
cwd: opts.cwd,
|
|
2534
|
-
model: opts.model,
|
|
2535
|
-
currentUserPrompt: opts.currentUserPrompt,
|
|
2536
|
-
playwrightPort: opts.playwrightPort,
|
|
2537
|
-
playwrightCapability: opts.playwrightCapability,
|
|
2538
|
-
autoContinue: false,
|
|
2539
|
-
visualTools: opts.visualTools,
|
|
2540
|
-
fileDeliveryTools: opts.fileDeliveryTools
|
|
2541
|
-
});
|
|
2542
|
-
}
|
|
2543
|
-
function getMcpServersForQuery(opts) {
|
|
2544
|
-
if (opts.sessionType === "cron") {
|
|
2545
|
-
if (!opts.topicId)
|
|
2546
|
-
throw new Error("getMcpServersForQuery: cron sessionType requires topicId");
|
|
2547
|
-
return getCronMcpServers({
|
|
2548
|
-
userId: opts.userId || "default",
|
|
2549
|
-
session: opts.session || "cron",
|
|
2550
|
-
topicId: opts.topicId,
|
|
2551
|
-
queryId: opts.queryId,
|
|
2552
|
-
wikiTopicId: opts.wikiTopicId,
|
|
2553
|
-
agent: opts.agent,
|
|
2554
|
-
cwd: opts.cwd,
|
|
2555
|
-
model: opts.model,
|
|
2556
|
-
currentUserPrompt: opts.prompt,
|
|
2557
|
-
playwrightPort: opts.playwrightPort,
|
|
2558
|
-
playwrightCapability: opts.playwrightCapability,
|
|
2559
|
-
visualTools: opts.visualTools,
|
|
2560
|
-
fileDeliveryTools: opts.fileDeliveryTools
|
|
2561
|
-
});
|
|
2562
|
-
}
|
|
2563
|
-
if (opts.sessionType === "dm" || opts.sessionType === "ephemeral") {
|
|
2564
|
-
return getDmMcpServers({
|
|
2565
|
-
userId: opts.userId || "default",
|
|
2566
|
-
agent: opts.agent,
|
|
2567
|
-
playwrightPort: opts.playwrightPort,
|
|
2568
|
-
playwrightCapability: opts.playwrightCapability
|
|
2569
|
-
});
|
|
2570
|
-
}
|
|
2571
|
-
if (opts.sessionType === "manager") {
|
|
2572
|
-
return getManagerMcpServers({
|
|
2573
|
-
userId: opts.userId || "default",
|
|
2574
|
-
session: opts.session,
|
|
2575
|
-
topicId: opts.topicId,
|
|
2576
|
-
queryId: opts.queryId,
|
|
2577
|
-
wikiTopicId: opts.wikiTopicId,
|
|
2578
|
-
agent: opts.agent,
|
|
2579
|
-
cwd: opts.cwd,
|
|
2580
|
-
model: opts.model,
|
|
2581
|
-
currentUserPrompt: opts.prompt,
|
|
2582
|
-
playwrightPort: opts.playwrightPort,
|
|
2583
|
-
playwrightCapability: opts.playwrightCapability,
|
|
2584
|
-
autoContinue: opts.autoContinue,
|
|
2585
|
-
visualTools: opts.visualTools,
|
|
2586
|
-
fileDeliveryTools: opts.fileDeliveryTools
|
|
2587
|
-
});
|
|
2588
|
-
}
|
|
2589
|
-
return getForumMcpServers({
|
|
2590
|
-
userId: opts.userId || "default",
|
|
2591
|
-
session: opts.session || "default",
|
|
2592
|
-
topicId: opts.topicId,
|
|
2593
|
-
queryId: opts.queryId,
|
|
2594
|
-
wikiTopicId: opts.wikiTopicId,
|
|
2595
|
-
agent: opts.agent,
|
|
2596
|
-
cwd: opts.cwd,
|
|
2597
|
-
model: opts.model,
|
|
2598
|
-
currentUserPrompt: opts.prompt,
|
|
2599
|
-
playwrightPort: opts.playwrightPort,
|
|
2600
|
-
playwrightCapability: opts.playwrightCapability,
|
|
2601
|
-
bgBashPort: opts.bgBashPort,
|
|
2602
|
-
autoContinue: opts.autoContinue,
|
|
2603
|
-
visualTools: opts.visualTools,
|
|
2604
|
-
fileDeliveryTools: opts.fileDeliveryTools,
|
|
2605
|
-
depth: opts.depth,
|
|
2606
|
-
enabled: opts.mcpEnabled,
|
|
2607
|
-
extra: opts.mcpExtra,
|
|
2608
|
-
silent: opts.silent,
|
|
2609
|
-
peerBridge: opts.peerBridge
|
|
2610
|
-
});
|
|
2611
|
-
}
|
|
2612
|
-
|
|
2613
|
-
// ../../packages/core/src/storage/vault.ts
|
|
2614
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7 } from "fs";
|
|
2615
|
-
import { join as join10 } from "path";
|
|
2616
|
-
|
|
2617
|
-
// ../../packages/core/src/storage/vault-crypto.ts
|
|
2618
|
-
import { createCipheriv, createDecipheriv, createHash, randomBytes as randomBytes4 } from "crypto";
|
|
2619
|
-
var ENVELOPE_PREFIX = "otium-vault:v1:";
|
|
2620
|
-
var IV_BYTES = 12;
|
|
2621
|
-
var KEY_BYTES = 32;
|
|
2622
|
-
function encryptionKey(masterKey = VAULT_MASTER_KEY) {
|
|
2623
|
-
return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(masterKey, "utf8").digest().subarray(0, KEY_BYTES);
|
|
2624
|
-
}
|
|
2625
|
-
function aad(userId, key) {
|
|
2626
|
-
return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
|
|
2627
|
-
}
|
|
2628
|
-
function isEncryptedVaultValue(value) {
|
|
2629
|
-
return value.startsWith(ENVELOPE_PREFIX);
|
|
2630
|
-
}
|
|
2631
|
-
function encryptVaultValue(userId, key, value, masterKey = VAULT_MASTER_KEY) {
|
|
2632
|
-
const iv = randomBytes4(IV_BYTES);
|
|
2633
|
-
const cipher = createCipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
2634
|
-
cipher.setAAD(aad(userId, key));
|
|
2635
|
-
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
2636
|
-
const tag = cipher.getAuthTag();
|
|
2637
|
-
return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
|
|
2638
|
-
}
|
|
2639
|
-
function decryptVaultValue(userId, key, storedValue, masterKey = VAULT_MASTER_KEY) {
|
|
2640
|
-
if (!isEncryptedVaultValue(storedValue)) {
|
|
2641
|
-
return { value: storedValue, legacyPlaintext: true };
|
|
2642
|
-
}
|
|
2643
|
-
const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
|
|
2644
|
-
const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
|
|
2645
|
-
if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
|
|
2646
|
-
throw new Error("Invalid encrypted vault value");
|
|
2647
|
-
}
|
|
2648
|
-
const iv = Buffer.from(ivPart, "base64url");
|
|
2649
|
-
const ciphertext = Buffer.from(ciphertextPart, "base64url");
|
|
2650
|
-
const tag = Buffer.from(tagPart, "base64url");
|
|
2651
|
-
if (iv.length !== IV_BYTES || tag.length !== 16) {
|
|
2652
|
-
throw new Error("Invalid encrypted vault value");
|
|
2653
|
-
}
|
|
2654
|
-
const decipher = createDecipheriv("aes-256-gcm", encryptionKey(masterKey), iv);
|
|
2655
|
-
decipher.setAAD(aad(userId, key));
|
|
2656
|
-
decipher.setAuthTag(tag);
|
|
2657
|
-
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
2658
|
-
return { value: plaintext.toString("utf8"), legacyPlaintext: false };
|
|
2659
|
-
}
|
|
2660
|
-
|
|
2661
|
-
// ../../packages/core/src/storage/vault.ts
|
|
2662
|
-
var vaultDb;
|
|
2663
|
-
var vaultMasterKey = VAULT_MASTER_KEY;
|
|
2664
|
-
function initializeVaultDatabase(database) {
|
|
2665
|
-
database.exec("PRAGMA journal_mode = WAL");
|
|
2666
|
-
database.exec("PRAGMA busy_timeout = 5000");
|
|
2667
|
-
database.exec(`
|
|
2668
|
-
CREATE TABLE IF NOT EXISTS vault (
|
|
2669
|
-
user_id TEXT NOT NULL,
|
|
2670
|
-
key TEXT NOT NULL,
|
|
2671
|
-
value TEXT NOT NULL,
|
|
2672
|
-
description TEXT NOT NULL DEFAULT '',
|
|
2673
|
-
PRIMARY KEY (user_id, key)
|
|
2674
|
-
)
|
|
2675
|
-
`);
|
|
2676
|
-
{
|
|
2677
|
-
const cols = database.prepare("PRAGMA table_info(vault)").all();
|
|
2678
|
-
const uid = cols.find((c) => c.name === "user_id");
|
|
2679
|
-
if (uid && uid.type.toUpperCase() === "INTEGER") {
|
|
2680
|
-
database.exec("BEGIN");
|
|
2681
|
-
database.exec(`
|
|
2682
|
-
CREATE TABLE vault_migrated (
|
|
2683
|
-
user_id TEXT NOT NULL,
|
|
2684
|
-
key TEXT NOT NULL,
|
|
2685
|
-
value TEXT NOT NULL,
|
|
2686
|
-
description TEXT NOT NULL DEFAULT '',
|
|
2687
|
-
PRIMARY KEY (user_id, key)
|
|
2688
|
-
)
|
|
2689
|
-
`);
|
|
2690
|
-
database.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
|
|
2691
|
-
database.exec("DROP TABLE vault");
|
|
2692
|
-
database.exec("ALTER TABLE vault_migrated RENAME TO vault");
|
|
2693
|
-
database.exec("COMMIT");
|
|
2694
|
-
}
|
|
2695
|
-
}
|
|
2696
|
-
}
|
|
2697
|
-
function openVaultDatabase(dataDir) {
|
|
2698
|
-
const path = join10(dataDir, "vault.db");
|
|
2699
|
-
mkdirSync7(dataDir, { recursive: true });
|
|
2700
|
-
const database = new Database(path, { create: true });
|
|
2701
|
-
chmodSync2(path, 384);
|
|
2702
|
-
initializeVaultDatabase(database);
|
|
2703
|
-
return database;
|
|
2704
|
-
}
|
|
2705
|
-
function activeVaultDatabase() {
|
|
2706
|
-
if (!vaultDb)
|
|
2707
|
-
vaultDb = openVaultDatabase(DATA_DIR);
|
|
2708
|
-
return vaultDb;
|
|
2709
|
-
}
|
|
2710
|
-
var VAULT_VALUE_MAX_BYTES = 64 * 1024;
|
|
2711
|
-
function normalizeVaultKey(key) {
|
|
2712
|
-
return key.trim().toUpperCase();
|
|
2713
|
-
}
|
|
2714
|
-
function decryptRow(userId, key, storedValue) {
|
|
2715
|
-
const database = activeVaultDatabase();
|
|
2716
|
-
const decoded = decryptVaultValue(userId, key, storedValue, vaultMasterKey);
|
|
2717
|
-
if (decoded.legacyPlaintext) {
|
|
2718
|
-
database.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value, vaultMasterKey), userId, key, storedValue);
|
|
2719
|
-
}
|
|
2720
|
-
return decoded.value;
|
|
2721
|
-
}
|
|
2722
|
-
function vaultListWithValues(userId) {
|
|
2723
|
-
const rows = activeVaultDatabase().prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
|
|
2724
|
-
return rows.map((row) => ({
|
|
2725
|
-
key: row.key,
|
|
2726
|
-
description: row.description,
|
|
2727
|
-
value: decryptRow(userId, row.key, row.value)
|
|
2728
|
-
}));
|
|
2864
|
+
return false;
|
|
2865
|
+
if (enabled === null)
|
|
2866
|
+
return true;
|
|
2867
|
+
return enabled.includes(name) || REQUIRED_FORUM_MCP_SERVERS.includes(name);
|
|
2868
|
+
};
|
|
2869
|
+
const base = buildScope("forum", {
|
|
2870
|
+
userId,
|
|
2871
|
+
session,
|
|
2872
|
+
topicId,
|
|
2873
|
+
queryId,
|
|
2874
|
+
wikiTopicId,
|
|
2875
|
+
agent,
|
|
2876
|
+
cwd,
|
|
2877
|
+
model,
|
|
2878
|
+
currentUserPrompt,
|
|
2879
|
+
depth,
|
|
2880
|
+
playwrightPort,
|
|
2881
|
+
playwrightCapability,
|
|
2882
|
+
bgBashPort,
|
|
2883
|
+
autoContinue,
|
|
2884
|
+
visualTools,
|
|
2885
|
+
fileDeliveryTools,
|
|
2886
|
+
silent,
|
|
2887
|
+
peerBridge
|
|
2888
|
+
}, filter);
|
|
2889
|
+
return { ...base, ...extra };
|
|
2729
2890
|
}
|
|
2730
|
-
function
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2891
|
+
function getCronMcpServers(opts) {
|
|
2892
|
+
return buildScope("cron", {
|
|
2893
|
+
userId: opts.userId,
|
|
2894
|
+
session: opts.session,
|
|
2895
|
+
topicId: opts.topicId,
|
|
2896
|
+
queryId: opts.queryId,
|
|
2897
|
+
wikiTopicId: opts.wikiTopicId ?? opts.topicId,
|
|
2898
|
+
agent: opts.agent,
|
|
2899
|
+
cwd: opts.cwd,
|
|
2900
|
+
model: opts.model,
|
|
2901
|
+
currentUserPrompt: opts.currentUserPrompt,
|
|
2902
|
+
playwrightPort: opts.playwrightPort,
|
|
2903
|
+
playwrightCapability: opts.playwrightCapability,
|
|
2904
|
+
autoContinue: false,
|
|
2905
|
+
visualTools: opts.visualTools,
|
|
2906
|
+
fileDeliveryTools: opts.fileDeliveryTools
|
|
2740
2907
|
});
|
|
2741
|
-
return { text: substituted, usedKeys: [...usedKeys] };
|
|
2742
|
-
}
|
|
2743
|
-
function encodedSecretForms(value) {
|
|
2744
|
-
const forms = new Set([
|
|
2745
|
-
value,
|
|
2746
|
-
encodeURIComponent(value),
|
|
2747
|
-
Buffer.from(value, "utf8").toString("base64"),
|
|
2748
|
-
Buffer.from(value, "utf8").toString("base64url"),
|
|
2749
|
-
Buffer.from(value, "utf8").toString("hex")
|
|
2750
|
-
]);
|
|
2751
|
-
forms.delete("");
|
|
2752
|
-
return [...forms].sort((a, b) => b.length - a.length);
|
|
2753
2908
|
}
|
|
2754
|
-
function
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2909
|
+
function getMcpServersForQuery(opts) {
|
|
2910
|
+
if (opts.sessionType === "cron") {
|
|
2911
|
+
if (!opts.topicId)
|
|
2912
|
+
throw new Error("getMcpServersForQuery: cron sessionType requires topicId");
|
|
2913
|
+
return getCronMcpServers({
|
|
2914
|
+
userId: opts.userId || "default",
|
|
2915
|
+
session: opts.session || "cron",
|
|
2916
|
+
topicId: opts.topicId,
|
|
2917
|
+
queryId: opts.queryId,
|
|
2918
|
+
wikiTopicId: opts.wikiTopicId,
|
|
2919
|
+
agent: opts.agent,
|
|
2920
|
+
cwd: opts.cwd,
|
|
2921
|
+
model: opts.model,
|
|
2922
|
+
currentUserPrompt: opts.prompt,
|
|
2923
|
+
playwrightPort: opts.playwrightPort,
|
|
2924
|
+
playwrightCapability: opts.playwrightCapability,
|
|
2925
|
+
visualTools: opts.visualTools,
|
|
2926
|
+
fileDeliveryTools: opts.fileDeliveryTools
|
|
2927
|
+
});
|
|
2766
2928
|
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
continue;
|
|
2775
|
-
}
|
|
2776
|
-
redacted += `[REDACTED:${match.key}]`;
|
|
2777
|
-
offset += match.form.length;
|
|
2929
|
+
if (opts.sessionType === "dm" || opts.sessionType === "ephemeral") {
|
|
2930
|
+
return getDmMcpServers({
|
|
2931
|
+
userId: opts.userId || "default",
|
|
2932
|
+
agent: opts.agent,
|
|
2933
|
+
playwrightPort: opts.playwrightPort,
|
|
2934
|
+
playwrightCapability: opts.playwrightCapability
|
|
2935
|
+
});
|
|
2778
2936
|
}
|
|
2779
|
-
|
|
2937
|
+
if (opts.sessionType === "manager") {
|
|
2938
|
+
return getManagerMcpServers({
|
|
2939
|
+
userId: opts.userId || "default",
|
|
2940
|
+
session: opts.session,
|
|
2941
|
+
topicId: opts.topicId,
|
|
2942
|
+
queryId: opts.queryId,
|
|
2943
|
+
wikiTopicId: opts.wikiTopicId,
|
|
2944
|
+
agent: opts.agent,
|
|
2945
|
+
cwd: opts.cwd,
|
|
2946
|
+
model: opts.model,
|
|
2947
|
+
currentUserPrompt: opts.prompt,
|
|
2948
|
+
playwrightPort: opts.playwrightPort,
|
|
2949
|
+
playwrightCapability: opts.playwrightCapability,
|
|
2950
|
+
autoContinue: opts.autoContinue,
|
|
2951
|
+
visualTools: opts.visualTools,
|
|
2952
|
+
fileDeliveryTools: opts.fileDeliveryTools
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
return getForumMcpServers({
|
|
2956
|
+
userId: opts.userId || "default",
|
|
2957
|
+
session: opts.session || "default",
|
|
2958
|
+
topicId: opts.topicId,
|
|
2959
|
+
queryId: opts.queryId,
|
|
2960
|
+
wikiTopicId: opts.wikiTopicId,
|
|
2961
|
+
agent: opts.agent,
|
|
2962
|
+
cwd: opts.cwd,
|
|
2963
|
+
model: opts.model,
|
|
2964
|
+
currentUserPrompt: opts.prompt,
|
|
2965
|
+
playwrightPort: opts.playwrightPort,
|
|
2966
|
+
playwrightCapability: opts.playwrightCapability,
|
|
2967
|
+
bgBashPort: opts.bgBashPort,
|
|
2968
|
+
autoContinue: opts.autoContinue,
|
|
2969
|
+
visualTools: opts.visualTools,
|
|
2970
|
+
fileDeliveryTools: opts.fileDeliveryTools,
|
|
2971
|
+
depth: opts.depth,
|
|
2972
|
+
enabled: opts.mcpEnabled,
|
|
2973
|
+
extra: opts.mcpExtra,
|
|
2974
|
+
silent: opts.silent,
|
|
2975
|
+
peerBridge: opts.peerBridge
|
|
2976
|
+
});
|
|
2780
2977
|
}
|
|
2781
2978
|
|
|
2782
2979
|
// ../../packages/core/src/agents/execution-host.ts
|
|
@@ -3218,12 +3415,6 @@ async function* claudeProvider(opts) {
|
|
|
3218
3415
|
const m = message;
|
|
3219
3416
|
if (m.subtype === "init") {
|
|
3220
3417
|
yield { type: "session", sessionId: m.session_id };
|
|
3221
|
-
} else if (m.subtype === "task_started") {
|
|
3222
|
-
const t = m;
|
|
3223
|
-
if (t.subagent_type && !t.skip_transcript) {
|
|
3224
|
-
const desc = t.description ? ` ${t.description.slice(0, 60)}` : "";
|
|
3225
|
-
yield { type: "status", content: `\u25B6 [${t.subagent_type}]${desc}` };
|
|
3226
|
-
}
|
|
3227
3418
|
} else {
|
|
3228
3419
|
logger.debug({ subtype: m.subtype, msg: m }, "claudeProvider: system message (unhandled subtype)");
|
|
3229
3420
|
}
|
|
@@ -3307,7 +3498,8 @@ async function* claudeProvider(opts) {
|
|
|
3307
3498
|
yield {
|
|
3308
3499
|
type: "tool_result",
|
|
3309
3500
|
toolUseId: trBlock.tool_use_id || "",
|
|
3310
|
-
content: trContent
|
|
3501
|
+
content: trContent,
|
|
3502
|
+
...trBlock.is_error ? { isError: true } : {}
|
|
3311
3503
|
};
|
|
3312
3504
|
}
|
|
3313
3505
|
}
|
|
@@ -3321,39 +3513,98 @@ async function* claudeProvider(opts) {
|
|
|
3321
3513
|
}
|
|
3322
3514
|
|
|
3323
3515
|
// ../../packages/core/src/agents/codex-provider.ts
|
|
3324
|
-
import {
|
|
3516
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
3517
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, realpathSync as realpathSync3, statSync as statSync3 } from "fs";
|
|
3518
|
+
import { isAbsolute, relative, resolve as resolve6 } from "path";
|
|
3325
3519
|
import { Codex } from "@openai/codex-sdk";
|
|
3326
3520
|
|
|
3327
3521
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
3328
3522
|
import { spawn as spawn3 } from "child_process";
|
|
3523
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3329
3524
|
import {
|
|
3330
3525
|
chmodSync as chmodSync3,
|
|
3526
|
+
copyFileSync,
|
|
3331
3527
|
existsSync as existsSync9,
|
|
3528
|
+
mkdtempSync,
|
|
3332
3529
|
readFileSync as readFileSync7,
|
|
3333
3530
|
renameSync as renameSync4,
|
|
3531
|
+
rmSync,
|
|
3334
3532
|
unlinkSync as unlinkSync8,
|
|
3335
3533
|
writeFileSync as writeFileSync5
|
|
3336
3534
|
} from "fs";
|
|
3337
3535
|
import { createRequire } from "module";
|
|
3338
|
-
import {
|
|
3536
|
+
import { tmpdir } from "os";
|
|
3537
|
+
import { dirname as dirname7, join as join11 } from "path";
|
|
3339
3538
|
|
|
3340
3539
|
// ../../packages/core/src/version.ts
|
|
3341
|
-
var NEGOTIUM_VERSION = "0.1.
|
|
3540
|
+
var NEGOTIUM_VERSION = "0.1.30";
|
|
3342
3541
|
|
|
3343
3542
|
// ../../packages/core/src/agents/codex-native-multi-agent.ts
|
|
3344
|
-
var NEGOTIUM_MODEL_CATALOG = "negotium-model-catalog.json";
|
|
3345
3543
|
var moduleRequire = createRequire(import.meta.url);
|
|
3544
|
+
var codexSdkPackagePath = moduleRequire.resolve("@openai/codex-sdk/package.json");
|
|
3545
|
+
var codexSdkRequire = createRequire(codexSdkPackagePath);
|
|
3546
|
+
var bundledCodexPackagePath = codexSdkRequire.resolve("@openai/codex/package.json");
|
|
3547
|
+
function readPackageVersion(packageJsonPath) {
|
|
3548
|
+
const parsed = JSON.parse(readFileSync7(packageJsonPath, "utf8"));
|
|
3549
|
+
if (typeof parsed.version !== "string" || !parsed.version.trim()) {
|
|
3550
|
+
throw new Error(`Codex package has no valid version: ${packageJsonPath}`);
|
|
3551
|
+
}
|
|
3552
|
+
return parsed.version;
|
|
3553
|
+
}
|
|
3554
|
+
var BUNDLED_CODEX_VERSION = readPackageVersion(bundledCodexPackagePath);
|
|
3555
|
+
var SAFE_BUNDLED_CODEX_VERSION = BUNDLED_CODEX_VERSION.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
3556
|
+
var NEGOTIUM_MODEL_CACHE = `negotium-models-cache-${SAFE_BUNDLED_CODEX_VERSION}.json`;
|
|
3557
|
+
var NEGOTIUM_MODEL_CATALOG = `negotium-model-catalog-${SAFE_BUNDLED_CODEX_VERSION}.json`;
|
|
3346
3558
|
function codexCliScriptPath() {
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3559
|
+
return join11(dirname7(bundledCodexPackagePath), "bin", "codex.js");
|
|
3560
|
+
}
|
|
3561
|
+
function parseCodexModelCache(contents, sourcePath) {
|
|
3562
|
+
let parsed;
|
|
3563
|
+
try {
|
|
3564
|
+
parsed = JSON.parse(contents);
|
|
3565
|
+
} catch (error) {
|
|
3566
|
+
throw new Error(`Codex model cache is invalid JSON: ${sourcePath}`, { cause: error });
|
|
3567
|
+
}
|
|
3568
|
+
if (!Array.isArray(parsed.models) || parsed.models.length === 0) {
|
|
3569
|
+
throw new Error(`Codex model cache has no models: ${sourcePath}`);
|
|
3570
|
+
}
|
|
3571
|
+
return parsed;
|
|
3572
|
+
}
|
|
3573
|
+
function readCodexModelCache(cachePath) {
|
|
3574
|
+
const contents = readFileSync7(cachePath, "utf8");
|
|
3575
|
+
return { contents, parsed: parseCodexModelCache(contents, cachePath) };
|
|
3576
|
+
}
|
|
3577
|
+
function readCompatibleCodexModelCache(cachePath) {
|
|
3578
|
+
const cache = readCodexModelCache(cachePath);
|
|
3579
|
+
if (cache.parsed.client_version !== BUNDLED_CODEX_VERSION) {
|
|
3580
|
+
const found = typeof cache.parsed.client_version === "string" ? cache.parsed.client_version : "missing or invalid";
|
|
3581
|
+
throw new Error(`Codex model cache version ${found} does not match Negotium's bundled Codex ${BUNDLED_CODEX_VERSION}: ${cachePath}`);
|
|
3582
|
+
}
|
|
3583
|
+
return cache;
|
|
3584
|
+
}
|
|
3585
|
+
function writePrivateFileAtomic(path, contents) {
|
|
3586
|
+
if (existsSync9(path) && readFileSync7(path, "utf8") === contents)
|
|
3587
|
+
return;
|
|
3588
|
+
const tempPath = `${path}.${process.pid}.${randomUUID2()}.tmp`;
|
|
3589
|
+
try {
|
|
3590
|
+
writeFileSync5(tempPath, contents, { encoding: "utf8", mode: 384 });
|
|
3591
|
+
renameSync4(tempPath, path);
|
|
3592
|
+
chmodSync3(path, 384);
|
|
3593
|
+
} finally {
|
|
3594
|
+
try {
|
|
3595
|
+
unlinkSync8(tempPath);
|
|
3596
|
+
} catch {}
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
function bundledCodexModelCachePath(authFilePath) {
|
|
3600
|
+
return join11(dirname7(authFilePath), NEGOTIUM_MODEL_CACHE);
|
|
3350
3601
|
}
|
|
3351
3602
|
async function bootstrapCodexModelCache(codexHome, cachePath) {
|
|
3352
3603
|
const child = spawn3(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
|
|
3353
3604
|
env: { ...process.env, CODEX_HOME: codexHome },
|
|
3354
3605
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3355
3606
|
});
|
|
3356
|
-
await new Promise((
|
|
3607
|
+
await new Promise((resolve6, reject) => {
|
|
3357
3608
|
let settled = false;
|
|
3358
3609
|
let stdoutBuffer = "";
|
|
3359
3610
|
let stderr = "";
|
|
@@ -3372,7 +3623,7 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
|
|
|
3372
3623
|
else if (!existsSync9(cachePath))
|
|
3373
3624
|
reject(new Error("Codex did not create its model cache"));
|
|
3374
3625
|
else
|
|
3375
|
-
|
|
3626
|
+
resolve6();
|
|
3376
3627
|
};
|
|
3377
3628
|
const send = (message) => {
|
|
3378
3629
|
child.stdin.write(`${JSON.stringify(message)}
|
|
@@ -3430,25 +3681,59 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
|
|
|
3430
3681
|
});
|
|
3431
3682
|
});
|
|
3432
3683
|
}
|
|
3684
|
+
async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
|
|
3685
|
+
const sourceHome = dirname7(authFilePath);
|
|
3686
|
+
const isolatedHome = mkdtempSync(join11(tmpdir(), "negotium-codex-models-"));
|
|
3687
|
+
const isolatedCachePath = join11(isolatedHome, "models_cache.json");
|
|
3688
|
+
try {
|
|
3689
|
+
const isolatedAuthPath = join11(isolatedHome, "auth.json");
|
|
3690
|
+
copyFileSync(authFilePath, isolatedAuthPath);
|
|
3691
|
+
chmodSync3(isolatedAuthPath, 384);
|
|
3692
|
+
const sourceConfigPath = join11(sourceHome, "config.toml");
|
|
3693
|
+
if (existsSync9(sourceConfigPath)) {
|
|
3694
|
+
const isolatedConfigPath = join11(isolatedHome, "config.toml");
|
|
3695
|
+
copyFileSync(sourceConfigPath, isolatedConfigPath);
|
|
3696
|
+
chmodSync3(isolatedConfigPath, 384);
|
|
3697
|
+
}
|
|
3698
|
+
await bootstrap(isolatedHome, isolatedCachePath);
|
|
3699
|
+
return readCompatibleCodexModelCache(isolatedCachePath).contents;
|
|
3700
|
+
} finally {
|
|
3701
|
+
rmSync(isolatedHome, { recursive: true, force: true });
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3433
3704
|
async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexModelCache) {
|
|
3434
|
-
const codexHome =
|
|
3435
|
-
const
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3705
|
+
const codexHome = dirname7(authFilePath);
|
|
3706
|
+
const configuredCachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE;
|
|
3707
|
+
if (configuredCachePath) {
|
|
3708
|
+
if (!existsSync9(configuredCachePath)) {
|
|
3709
|
+
throw new Error(`Configured Codex model cache does not exist: ${configuredCachePath}`);
|
|
3710
|
+
}
|
|
3711
|
+
readCompatibleCodexModelCache(configuredCachePath);
|
|
3712
|
+
return configuredCachePath;
|
|
3713
|
+
}
|
|
3714
|
+
const bundledCachePath = bundledCodexModelCachePath(authFilePath);
|
|
3715
|
+
const sharedCachePath = join11(codexHome, "models_cache.json");
|
|
3716
|
+
if (existsSync9(sharedCachePath)) {
|
|
3717
|
+
try {
|
|
3718
|
+
const shared = readCompatibleCodexModelCache(sharedCachePath);
|
|
3719
|
+
writePrivateFileAtomic(bundledCachePath, shared.contents);
|
|
3720
|
+
return bundledCachePath;
|
|
3721
|
+
} catch {}
|
|
3722
|
+
}
|
|
3723
|
+
if (existsSync9(bundledCachePath)) {
|
|
3724
|
+
try {
|
|
3725
|
+
readCompatibleCodexModelCache(bundledCachePath);
|
|
3726
|
+
return bundledCachePath;
|
|
3727
|
+
} catch {}
|
|
3441
3728
|
}
|
|
3442
|
-
await
|
|
3729
|
+
const refreshedContents = await bootstrapIsolatedCodexModelCache(authFilePath, bootstrap);
|
|
3730
|
+
writePrivateFileAtomic(bundledCachePath, refreshedContents);
|
|
3731
|
+
return bundledCachePath;
|
|
3443
3732
|
}
|
|
3444
|
-
function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath) {
|
|
3445
|
-
const codexHome =
|
|
3446
|
-
const sourcePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE ?? (existsSync9(join11(codexHome, "models_cache.json")) ? join11(codexHome, "models_cache.json") : join11(codexHome, NEGOTIUM_MODEL_CATALOG));
|
|
3733
|
+
function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath, sourcePath) {
|
|
3734
|
+
const codexHome = dirname7(authFilePath);
|
|
3447
3735
|
const outputPath = join11(codexHome, NEGOTIUM_MODEL_CATALOG);
|
|
3448
|
-
const parsed =
|
|
3449
|
-
if (!Array.isArray(parsed.models) || parsed.models.length === 0) {
|
|
3450
|
-
throw new Error(`Codex model cache has no models: ${sourcePath}`);
|
|
3451
|
-
}
|
|
3736
|
+
const parsed = readCodexModelCache(sourcePath).parsed;
|
|
3452
3737
|
const models = parsed.models.map((model, index) => {
|
|
3453
3738
|
if (!model || typeof model !== "object" || Array.isArray(model)) {
|
|
3454
3739
|
throw new Error(`Codex model cache entry ${index} is invalid: ${sourcePath}`);
|
|
@@ -3457,22 +3742,56 @@ function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath) {
|
|
|
3457
3742
|
});
|
|
3458
3743
|
const contents = `${JSON.stringify({ models }, null, 2)}
|
|
3459
3744
|
`;
|
|
3460
|
-
|
|
3461
|
-
return outputPath;
|
|
3462
|
-
}
|
|
3463
|
-
const tempPath = `${outputPath}.${process.pid}.${Date.now()}.tmp`;
|
|
3464
|
-
try {
|
|
3465
|
-
writeFileSync5(tempPath, contents, { encoding: "utf8", mode: 384 });
|
|
3466
|
-
renameSync4(tempPath, outputPath);
|
|
3467
|
-
chmodSync3(outputPath, 384);
|
|
3468
|
-
} finally {
|
|
3469
|
-
try {
|
|
3470
|
-
unlinkSync8(tempPath);
|
|
3471
|
-
} catch {}
|
|
3472
|
-
}
|
|
3745
|
+
writePrivateFileAtomic(outputPath, contents);
|
|
3473
3746
|
return outputPath;
|
|
3474
3747
|
}
|
|
3475
3748
|
|
|
3749
|
+
// ../../packages/core/src/agents/tool-format.ts
|
|
3750
|
+
import { diffLines as computeLineDiff } from "diff";
|
|
3751
|
+
function changedDiffLines(value) {
|
|
3752
|
+
const lines = value.split(`
|
|
3753
|
+
`);
|
|
3754
|
+
if (lines.at(-1) === "")
|
|
3755
|
+
lines.pop();
|
|
3756
|
+
return lines;
|
|
3757
|
+
}
|
|
3758
|
+
function buildNumberedDiffSummary(before, after, startLine = 1) {
|
|
3759
|
+
const removed = [];
|
|
3760
|
+
const added = [];
|
|
3761
|
+
const preview = [];
|
|
3762
|
+
let oldLine = startLine;
|
|
3763
|
+
let newLine = startLine;
|
|
3764
|
+
for (const part of computeLineDiff(before, after)) {
|
|
3765
|
+
const lines = changedDiffLines(part.value);
|
|
3766
|
+
if (part.removed) {
|
|
3767
|
+
for (const line of lines) {
|
|
3768
|
+
removed.push(line);
|
|
3769
|
+
preview.push(`${oldLine} -${line}`);
|
|
3770
|
+
oldLine += 1;
|
|
3771
|
+
}
|
|
3772
|
+
continue;
|
|
3773
|
+
}
|
|
3774
|
+
if (part.added) {
|
|
3775
|
+
for (const line of lines) {
|
|
3776
|
+
added.push(line);
|
|
3777
|
+
preview.push(`${newLine} +${line}`);
|
|
3778
|
+
newLine += 1;
|
|
3779
|
+
}
|
|
3780
|
+
continue;
|
|
3781
|
+
}
|
|
3782
|
+
oldLine += lines.length;
|
|
3783
|
+
newLine += lines.length;
|
|
3784
|
+
}
|
|
3785
|
+
return {
|
|
3786
|
+
...removed.length > 0 ? { before: removed.join(`
|
|
3787
|
+
`) } : {},
|
|
3788
|
+
...added.length > 0 ? { after: added.join(`
|
|
3789
|
+
`) } : {},
|
|
3790
|
+
...preview.length > 0 ? { diffPreview: preview.join(`
|
|
3791
|
+
`) } : {}
|
|
3792
|
+
};
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3476
3795
|
// ../../packages/core/src/agents/codex-provider.ts
|
|
3477
3796
|
function mapEffort(effort) {
|
|
3478
3797
|
if (!effort)
|
|
@@ -3555,22 +3874,142 @@ function summarizeMcpToolCallResult(item) {
|
|
|
3555
3874
|
return text.slice(0, 200);
|
|
3556
3875
|
return JSON.stringify(item.result.structured_content ?? "").slice(0, 200);
|
|
3557
3876
|
}
|
|
3558
|
-
|
|
3877
|
+
var CODEX_DIFF_FILE_LIMIT = 512 * 1024;
|
|
3878
|
+
var CODEX_DIFF_BASELINE_FILE_LIMIT = 200;
|
|
3879
|
+
var CODEX_DIFF_BASELINE_BYTE_LIMIT = 16 * 1024 * 1024;
|
|
3880
|
+
function canonicalPath(path) {
|
|
3881
|
+
try {
|
|
3882
|
+
return realpathSync3(resolve6(path));
|
|
3883
|
+
} catch {
|
|
3884
|
+
return resolve6(path);
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
function textFile(path, maxBytes = CODEX_DIFF_FILE_LIMIT) {
|
|
3888
|
+
if (!existsSync10(path))
|
|
3889
|
+
return null;
|
|
3890
|
+
try {
|
|
3891
|
+
const byteLimit = Math.min(CODEX_DIFF_FILE_LIMIT, Math.max(0, maxBytes));
|
|
3892
|
+
if (statSync3(path).size > byteLimit)
|
|
3893
|
+
return;
|
|
3894
|
+
const content = readFileSync8(path);
|
|
3895
|
+
if (content.byteLength > byteLimit || content.includes(0))
|
|
3896
|
+
return;
|
|
3897
|
+
return content.toString("utf8");
|
|
3898
|
+
} catch {
|
|
3899
|
+
return;
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
|
|
3903
|
+
class CodexFilePreviewTracker {
|
|
3904
|
+
#cwd;
|
|
3905
|
+
#root;
|
|
3906
|
+
#baseline = new Map;
|
|
3907
|
+
#baselineAvailable = false;
|
|
3908
|
+
constructor(cwd) {
|
|
3909
|
+
this.#cwd = canonicalPath(cwd);
|
|
3910
|
+
const root = this.#git(["rev-parse", "--show-toplevel"], this.#cwd)?.trim();
|
|
3911
|
+
this.#root = root ? canonicalPath(root) : null;
|
|
3912
|
+
if (!this.#root)
|
|
3913
|
+
return;
|
|
3914
|
+
const status = this.#git(["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
|
|
3915
|
+
if (status === undefined)
|
|
3916
|
+
return;
|
|
3917
|
+
this.#baselineAvailable = true;
|
|
3918
|
+
let loadedFiles = 0;
|
|
3919
|
+
let loadedBytes = 0;
|
|
3920
|
+
const records = status.split("\x00");
|
|
3921
|
+
for (let index = 0;index < records.length; index += 1) {
|
|
3922
|
+
const record = records[index];
|
|
3923
|
+
if (!record || record.length < 4)
|
|
3924
|
+
continue;
|
|
3925
|
+
const code = record.slice(0, 2);
|
|
3926
|
+
const path = record.slice(3);
|
|
3927
|
+
const remainingBytes = CODEX_DIFF_BASELINE_BYTE_LIMIT - loadedBytes;
|
|
3928
|
+
const content = loadedFiles < CODEX_DIFF_BASELINE_FILE_LIMIT && remainingBytes > 0 ? textFile(resolve6(this.#root, path), remainingBytes) : undefined;
|
|
3929
|
+
this.#baseline.set(path, content);
|
|
3930
|
+
if (typeof content === "string") {
|
|
3931
|
+
loadedFiles += 1;
|
|
3932
|
+
loadedBytes += Buffer.byteLength(content);
|
|
3933
|
+
}
|
|
3934
|
+
if (/[RC]/.test(code))
|
|
3935
|
+
index += 1;
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
preview(path, succeeded) {
|
|
3939
|
+
if (!this.#root || !this.#baselineAvailable || !succeeded)
|
|
3940
|
+
return {};
|
|
3941
|
+
const absolute = canonicalPath(isAbsolute(path) ? path : resolve6(this.#cwd, path));
|
|
3942
|
+
const relativePath = relative(this.#root, absolute);
|
|
3943
|
+
if (!relativePath || relativePath.startsWith("..") || isAbsolute(relativePath))
|
|
3944
|
+
return {};
|
|
3945
|
+
const before = this.#baseline.has(relativePath) ? this.#baseline.get(relativePath) : this.#headFile(relativePath);
|
|
3946
|
+
const after = textFile(absolute);
|
|
3947
|
+
this.#baseline.set(relativePath, after);
|
|
3948
|
+
if (before === undefined || after === undefined)
|
|
3949
|
+
return {};
|
|
3950
|
+
const diff = buildNumberedDiffSummary(before ?? "", after ?? "");
|
|
3951
|
+
return {
|
|
3952
|
+
...diff.before !== undefined ? { before: diff.before } : {},
|
|
3953
|
+
...diff.after !== undefined ? { after: diff.after } : {},
|
|
3954
|
+
...diff.diffPreview !== undefined ? { diff_preview: diff.diffPreview } : {}
|
|
3955
|
+
};
|
|
3956
|
+
}
|
|
3957
|
+
#headFile(path) {
|
|
3958
|
+
const content = this.#git(["show", `HEAD:${path}`]);
|
|
3959
|
+
if (content === undefined)
|
|
3960
|
+
return null;
|
|
3961
|
+
return Buffer.byteLength(content) > CODEX_DIFF_FILE_LIMIT || content.includes("\x00") ? undefined : content;
|
|
3962
|
+
}
|
|
3963
|
+
#git(args, cwd = this.#root ?? undefined) {
|
|
3964
|
+
try {
|
|
3965
|
+
return execFileSync5("git", ["-C", cwd ?? ".", ...args], {
|
|
3966
|
+
encoding: "utf8",
|
|
3967
|
+
maxBuffer: CODEX_DIFF_FILE_LIMIT * 4,
|
|
3968
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
3969
|
+
});
|
|
3970
|
+
} catch {
|
|
3971
|
+
return;
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
}
|
|
3975
|
+
async function fileChangeEvents(item, previews, cwd, threadId, consumedPatchCallIds) {
|
|
3976
|
+
const expectedPaths = item.changes.map((change) => isAbsolute(change.path) ? change.path : resolve6(cwd, change.path));
|
|
3977
|
+
let nativePreview;
|
|
3978
|
+
if (threadId && item.status === "completed") {
|
|
3979
|
+
await new Promise((resolveDelay) => setTimeout(resolveDelay, 20));
|
|
3980
|
+
nativePreview = readLatestCodexPatchPreview(threadId, expectedPaths, consumedPatchCallIds);
|
|
3981
|
+
}
|
|
3982
|
+
for (const delayMs of [20, 40, 80, 120]) {
|
|
3983
|
+
if (nativePreview || !threadId || item.status !== "completed")
|
|
3984
|
+
break;
|
|
3985
|
+
await new Promise((resolveDelay) => setTimeout(resolveDelay, delayMs));
|
|
3986
|
+
nativePreview = readLatestCodexPatchPreview(threadId, expectedPaths, consumedPatchCallIds);
|
|
3987
|
+
}
|
|
3988
|
+
if (nativePreview)
|
|
3989
|
+
consumedPatchCallIds.add(nativePreview.callId);
|
|
3559
3990
|
return item.changes.flatMap((change, index) => {
|
|
3560
3991
|
const name = change.kind === "add" ? "Write" : change.kind === "delete" ? "Delete" : "Edit";
|
|
3561
3992
|
const toolUseId = `${item.id}:${index}`;
|
|
3562
3993
|
const success = item.status === "completed";
|
|
3994
|
+
const nativeChange = nativePreview?.changes[index];
|
|
3995
|
+
const fallbackPreview = previews.preview(change.path, success);
|
|
3996
|
+
const preview = success && nativeChange ? {
|
|
3997
|
+
...nativeChange.before !== undefined ? { before: nativeChange.before } : {},
|
|
3998
|
+
...nativeChange.after !== undefined ? { after: nativeChange.after } : {},
|
|
3999
|
+
...nativeChange.diffPreview !== undefined ? { diff_preview: nativeChange.diffPreview } : {}
|
|
4000
|
+
} : fallbackPreview;
|
|
3563
4001
|
return [
|
|
3564
4002
|
{
|
|
3565
4003
|
type: "tool_use",
|
|
3566
4004
|
name,
|
|
3567
|
-
input: { file_path: change.path, change_kind: change.kind },
|
|
4005
|
+
input: { file_path: change.path, change_kind: change.kind, ...preview },
|
|
3568
4006
|
toolUseId
|
|
3569
4007
|
},
|
|
3570
4008
|
{
|
|
3571
4009
|
type: "tool_result",
|
|
3572
4010
|
toolUseId,
|
|
3573
|
-
content: success ? `${change.kind} applied: ${change.path}` : `${change.kind} failed: ${change.path}
|
|
4011
|
+
content: success ? `${change.kind} applied: ${change.path}` : `${change.kind} failed: ${change.path}`,
|
|
4012
|
+
...success ? {} : { isError: true }
|
|
3574
4013
|
}
|
|
3575
4014
|
];
|
|
3576
4015
|
});
|
|
@@ -3656,6 +4095,8 @@ function prependFirstEvent(iter, firstEvent, alreadyDone) {
|
|
|
3656
4095
|
};
|
|
3657
4096
|
}
|
|
3658
4097
|
async function* codexProvider(opts) {
|
|
4098
|
+
const filePreviews = new CodexFilePreviewTracker(opts.cwd);
|
|
4099
|
+
const consumedPatchCallIds = new Set(opts.sessionId ? readCodexPatchCallIds(opts.sessionId) : []);
|
|
3659
4100
|
const codexAuthPath = hostedCodexAuthFilePath();
|
|
3660
4101
|
if (!existsSync10(codexAuthPath)) {
|
|
3661
4102
|
yield {
|
|
@@ -3666,8 +4107,8 @@ async function* codexProvider(opts) {
|
|
|
3666
4107
|
}
|
|
3667
4108
|
let codexModelCatalogPath;
|
|
3668
4109
|
try {
|
|
3669
|
-
await ensureCodexModelCache(codexAuthPath);
|
|
3670
|
-
codexModelCatalogPath = writeCodexCatalogWithNativeMultiAgentDisabled(codexAuthPath);
|
|
4110
|
+
const codexModelCachePath = await ensureCodexModelCache(codexAuthPath);
|
|
4111
|
+
codexModelCatalogPath = writeCodexCatalogWithNativeMultiAgentDisabled(codexAuthPath, codexModelCachePath);
|
|
3671
4112
|
if (opts.sessionId)
|
|
3672
4113
|
migrateCodexRolloutNativeMultiAgentMetadata(opts.sessionId);
|
|
3673
4114
|
} catch (err) {
|
|
@@ -3790,10 +4231,11 @@ async function* codexProvider(opts) {
|
|
|
3790
4231
|
if (!item)
|
|
3791
4232
|
break;
|
|
3792
4233
|
if (item.type === "command_execution") {
|
|
4234
|
+
const command = String(item.command ?? "");
|
|
3793
4235
|
yield {
|
|
3794
4236
|
type: "tool_use",
|
|
3795
4237
|
name: "Bash",
|
|
3796
|
-
input: { command
|
|
4238
|
+
input: { command },
|
|
3797
4239
|
toolUseId: String(item.id ?? "")
|
|
3798
4240
|
};
|
|
3799
4241
|
} else if (item.type === "mcp_tool_call") {
|
|
@@ -3839,19 +4281,24 @@ async function* codexProvider(opts) {
|
|
|
3839
4281
|
if (reasoning)
|
|
3840
4282
|
yield { type: "reasoning", content: reasoning };
|
|
3841
4283
|
} else if (item.type === "mcp_tool_call") {
|
|
4284
|
+
const isError = item.status === "failed" || Boolean(item.error);
|
|
3842
4285
|
yield {
|
|
3843
4286
|
type: "tool_result",
|
|
3844
4287
|
toolUseId: String(item.id ?? ""),
|
|
3845
|
-
content: summarizeMcpToolCallResult(item)
|
|
4288
|
+
content: summarizeMcpToolCallResult(item),
|
|
4289
|
+
...isError ? { isError: true } : {}
|
|
3846
4290
|
};
|
|
3847
4291
|
} else if (item.type === "command_execution") {
|
|
4292
|
+
const isError = item.status === "failed" || typeof item.exit_code === "number" && item.exit_code !== 0;
|
|
3848
4293
|
yield {
|
|
3849
4294
|
type: "tool_result",
|
|
3850
4295
|
toolUseId: String(item.id ?? ""),
|
|
3851
|
-
content: String(item.aggregated_output ?? "").slice(0, 200)
|
|
4296
|
+
content: String(item.aggregated_output ?? "").slice(0, 200),
|
|
4297
|
+
...isError ? { isError: true } : {}
|
|
3852
4298
|
};
|
|
3853
4299
|
} else if (item.type === "file_change") {
|
|
3854
|
-
|
|
4300
|
+
const fileEvents = await fileChangeEvents(item, filePreviews, opts.cwd, currentSessionId, consumedPatchCallIds);
|
|
4301
|
+
for (const fileEvent of fileEvents) {
|
|
3855
4302
|
yield fileEvent;
|
|
3856
4303
|
}
|
|
3857
4304
|
} else if (item.type === "error") {
|
|
@@ -4000,6 +4447,18 @@ function providerOwnedToolRedirect(toolName) {
|
|
|
4000
4447
|
}
|
|
4001
4448
|
return "Use the shared task MCP tools instead " + "(mcp__task__task_create / task_update / task_list / task_get / task_delete).";
|
|
4002
4449
|
}
|
|
4450
|
+
function resolveMaestroApiKeyOverrides(userId) {
|
|
4451
|
+
if (!userId)
|
|
4452
|
+
return;
|
|
4453
|
+
const deepseek = vaultGetValue(userId, "DEEPSEEK_API_KEY")?.trim();
|
|
4454
|
+
const moonshot = vaultGetValue(userId, "MOONSHOT_API_KEY")?.trim();
|
|
4455
|
+
if (!deepseek && !moonshot)
|
|
4456
|
+
return;
|
|
4457
|
+
return {
|
|
4458
|
+
...deepseek ? { deepseek } : {},
|
|
4459
|
+
...moonshot ? { moonshot } : {}
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4003
4462
|
function buildProviderOwnedToolBlockHook() {
|
|
4004
4463
|
return {
|
|
4005
4464
|
name: "provider-owned-tool-redirect",
|
|
@@ -4026,6 +4485,7 @@ function maestroProvider(opts) {
|
|
|
4026
4485
|
maxTokens: MAESTRO_DEFAULT_MAX_TOKENS,
|
|
4027
4486
|
enableToolSearch: true,
|
|
4028
4487
|
...opts,
|
|
4488
|
+
apiKeyOverrides: resolveMaestroApiKeyOverrides(userId),
|
|
4029
4489
|
agent: "maestro",
|
|
4030
4490
|
disallowedTools: buildMaestroDisallowedTools(callerDisallowedTools),
|
|
4031
4491
|
toolHooks: [...buildMaestroToolHooks(userId), ...callerToolHooks]
|
|
@@ -4034,8 +4494,8 @@ function maestroProvider(opts) {
|
|
|
4034
4494
|
}
|
|
4035
4495
|
|
|
4036
4496
|
// ../../packages/core/src/storage/tasks.ts
|
|
4037
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as
|
|
4038
|
-
import { dirname as
|
|
4497
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync9, renameSync as renameSync5, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
4498
|
+
import { dirname as dirname8, join as join12 } from "path";
|
|
4039
4499
|
function safeUserIdComponent2(userId) {
|
|
4040
4500
|
const str = String(userId);
|
|
4041
4501
|
if (!str || /[/\\]|\.\./.test(str)) {
|
|
@@ -4061,7 +4521,7 @@ function readTasks(userId, scopeKey) {
|
|
|
4061
4521
|
if (!existsSync11(path))
|
|
4062
4522
|
return [];
|
|
4063
4523
|
try {
|
|
4064
|
-
const parsed = JSON.parse(
|
|
4524
|
+
const parsed = JSON.parse(readFileSync9(path, "utf-8"));
|
|
4065
4525
|
return Array.isArray(parsed?.tasks) ? parsed.tasks : [];
|
|
4066
4526
|
} catch (e) {
|
|
4067
4527
|
logger.warn({ err: e, path }, "tasks: failed to read task store");
|
|
@@ -4070,7 +4530,7 @@ function readTasks(userId, scopeKey) {
|
|
|
4070
4530
|
}
|
|
4071
4531
|
function taskFileMtimeNs(userId, scopeKey) {
|
|
4072
4532
|
try {
|
|
4073
|
-
return
|
|
4533
|
+
return statSync4(getTaskFilePath(userId, scopeKey), { bigint: true }).mtimeNs;
|
|
4074
4534
|
} catch {
|
|
4075
4535
|
return null;
|
|
4076
4536
|
}
|
|
@@ -4207,7 +4667,7 @@ async function* runAgent(opts) {
|
|
|
4207
4667
|
}
|
|
4208
4668
|
|
|
4209
4669
|
// ../../packages/core/src/bus.ts
|
|
4210
|
-
import { randomUUID as
|
|
4670
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4211
4671
|
|
|
4212
4672
|
// ../../packages/core/src/storage/forum-db.ts
|
|
4213
4673
|
var db = internalStorageDatabase;
|
|
@@ -4288,7 +4748,7 @@ class SqliteRuntimeBus {
|
|
|
4288
4748
|
pollTimer = null;
|
|
4289
4749
|
polling = false;
|
|
4290
4750
|
constructor(options = {}) {
|
|
4291
|
-
this.sourceId = options.sourceId ?? `${process.pid}-${
|
|
4751
|
+
this.sourceId = options.sourceId ?? `${process.pid}-${randomUUID3()}`;
|
|
4292
4752
|
this.pollIntervalMs = Math.max(25, options.pollIntervalMs ?? 100);
|
|
4293
4753
|
this.cursor = latestRuntimeEventSeq();
|
|
4294
4754
|
}
|
|
@@ -4391,9 +4851,15 @@ class SqliteRuntimeBus {
|
|
|
4391
4851
|
toolUseId: id
|
|
4392
4852
|
});
|
|
4393
4853
|
}
|
|
4394
|
-
broadcastToolOutput(topicId, queryId, toolUseId, content) {
|
|
4854
|
+
broadcastToolOutput(topicId, queryId, toolUseId, content, isError) {
|
|
4395
4855
|
const id = toolUseId.trim() || `${queryId}:tool`;
|
|
4396
|
-
this.broadcastAiStatus(topicId, {
|
|
4856
|
+
this.broadcastAiStatus(topicId, {
|
|
4857
|
+
kind: "tool_output",
|
|
4858
|
+
queryId,
|
|
4859
|
+
toolUseId: id,
|
|
4860
|
+
content,
|
|
4861
|
+
...isError ? { isError: true } : {}
|
|
4862
|
+
});
|
|
4397
4863
|
}
|
|
4398
4864
|
broadcastToolStatus(topicId, queryId, kind, content, meta) {
|
|
4399
4865
|
this.broadcastAiStatus(topicId, {
|
|
@@ -4435,8 +4901,8 @@ var WsHub = {
|
|
|
4435
4901
|
};
|
|
4436
4902
|
|
|
4437
4903
|
// ../../packages/core/src/prompts/builders.ts
|
|
4438
|
-
import { readFileSync as
|
|
4439
|
-
import { resolve as
|
|
4904
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
4905
|
+
import { resolve as resolve7 } from "path";
|
|
4440
4906
|
|
|
4441
4907
|
// ../../packages/core/src/agents/model-catalog.ts
|
|
4442
4908
|
var CODEX_PRO_20X_COST = "ChatGPT Pro 20x subscription: $200/month";
|
|
@@ -4537,10 +5003,10 @@ var SELECTABLE_MODELS = [
|
|
|
4537
5003
|
];
|
|
4538
5004
|
|
|
4539
5005
|
// ../../packages/core/src/prompts/builders.ts
|
|
4540
|
-
var PROMPTS_DIR =
|
|
4541
|
-
var SESSIONS_DIR =
|
|
5006
|
+
var PROMPTS_DIR = resolve7(PROJECT_ROOT, "src/prompts");
|
|
5007
|
+
var SESSIONS_DIR = resolve7(PROMPTS_DIR, "sessions");
|
|
4542
5008
|
function loadAgentPrompt(filename) {
|
|
4543
|
-
const raw =
|
|
5009
|
+
const raw = readFileSync10(resolve7(AGENTS_PROMPTS_DIR, filename), "utf-8");
|
|
4544
5010
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
4545
5011
|
if (!match)
|
|
4546
5012
|
throw new Error(`Agent prompt ${filename} is missing frontmatter`);
|
|
@@ -4721,7 +5187,7 @@ function setTopicBrief(topicId, fields) {
|
|
|
4721
5187
|
}
|
|
4722
5188
|
|
|
4723
5189
|
// ../../packages/core/src/storage/wiki.ts
|
|
4724
|
-
import { basename, dirname as
|
|
5190
|
+
import { basename as basename2, dirname as dirname9, join as join14 } from "path";
|
|
4725
5191
|
function getSharedWikiDir(workspaceDir = resolveStorageWorkspaceDir()) {
|
|
4726
5192
|
return workspaceDir === resolveStorageWorkspaceDir() ? resolveStorageSharedWikiDir() : join14(workspaceDir, "wiki");
|
|
4727
5193
|
}
|
|
@@ -4741,7 +5207,7 @@ function wikiSummaryFilename(date, rawTopic, topicId) {
|
|
|
4741
5207
|
}
|
|
4742
5208
|
|
|
4743
5209
|
// ../../packages/core/src/topics/personal-general.ts
|
|
4744
|
-
import { randomUUID as
|
|
5210
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4745
5211
|
|
|
4746
5212
|
// ../../packages/core/src/platform/constants.ts
|
|
4747
5213
|
var FROM_AUTO_CONTINUE = "auto-continue";
|
|
@@ -5250,7 +5716,7 @@ function ensurePersonalGeneral(userId) {
|
|
|
5250
5716
|
const now = new Date().toISOString();
|
|
5251
5717
|
const registry = getRegistry("maestro");
|
|
5252
5718
|
const topic = {
|
|
5253
|
-
id:
|
|
5719
|
+
id: randomUUID4(),
|
|
5254
5720
|
title: "General",
|
|
5255
5721
|
description: PERSONAL_GENERAL_DESCRIPTION,
|
|
5256
5722
|
kind: "manager",
|
|
@@ -5336,7 +5802,7 @@ function runArchiverTurn(params) {
|
|
|
5336
5802
|
mcpEnabled: ["wiki"],
|
|
5337
5803
|
silent: true
|
|
5338
5804
|
});
|
|
5339
|
-
const activeSessionId = `memory:${
|
|
5805
|
+
const activeSessionId = `memory:${randomUUID5()}`;
|
|
5340
5806
|
activeArchiverSessions.set(activeSessionId, {
|
|
5341
5807
|
id: activeSessionId,
|
|
5342
5808
|
kind: "memory",
|
|
@@ -5445,7 +5911,7 @@ function findSummaryFile(topicTitle, date, sinceMs, topicId) {
|
|
|
5445
5911
|
continue;
|
|
5446
5912
|
const p = join15(dir, f);
|
|
5447
5913
|
try {
|
|
5448
|
-
const m =
|
|
5914
|
+
const m = statSync5(p).mtimeMs;
|
|
5449
5915
|
if (m >= sinceMs && (!best || m > best.mtime))
|
|
5450
5916
|
best = { path: p, mtime: m };
|
|
5451
5917
|
} catch {}
|
|
@@ -5457,7 +5923,7 @@ function finalizeGeneralMemory(userId, topicTitle, messageCount, startMs, ok, to
|
|
|
5457
5923
|
const date = new Date().toISOString().slice(0, 10);
|
|
5458
5924
|
try {
|
|
5459
5925
|
const summaryPath = ok ? findSummaryFile(topicTitle, date, startMs, topicId) : null;
|
|
5460
|
-
const summaryMd = summaryPath ?
|
|
5926
|
+
const summaryMd = summaryPath ? readFileSync11(summaryPath, "utf-8") : "";
|
|
5461
5927
|
const oneLine = summaryMd && distillOneLine(summaryMd) || `${messageCount}\uAC1C \uBA54\uC2DC\uC9C0 \uC544\uCE74\uC774\uBE0C`;
|
|
5462
5928
|
const prev = getTopicBrief(generalTopicId);
|
|
5463
5929
|
const prevEntries = (prev?.briefMd ?? "").split(`
|
|
@@ -5492,7 +5958,7 @@ ${rolled.join(`
|
|
|
5492
5958
|
usage: generalReply.usage
|
|
5493
5959
|
} : { authorId: "system" };
|
|
5494
5960
|
const msg = {
|
|
5495
|
-
id:
|
|
5961
|
+
id: randomUUID5(),
|
|
5496
5962
|
topicId: generalTopicId,
|
|
5497
5963
|
text,
|
|
5498
5964
|
...replyMeta,
|
|
@@ -5526,7 +5992,7 @@ function countMemoryArchiveExchanges(rows) {
|
|
|
5526
5992
|
}
|
|
5527
5993
|
|
|
5528
5994
|
// ../../packages/core/src/storage/runtime-leases.ts
|
|
5529
|
-
import { randomUUID as
|
|
5995
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
5530
5996
|
|
|
5531
5997
|
// ../../packages/core/src/storage/runtime-topic-state.ts
|
|
5532
5998
|
var TOPIC_MAINTENANCE_STALE_MS = 30000;
|
|
@@ -5541,7 +6007,7 @@ db.exec(`
|
|
|
5541
6007
|
`);
|
|
5542
6008
|
|
|
5543
6009
|
// ../../packages/core/src/storage/runtime-leases.ts
|
|
5544
|
-
var RUNTIME_INSTANCE_ID = `${process.pid}-${
|
|
6010
|
+
var RUNTIME_INSTANCE_ID = `${process.pid}-${randomUUID6()}`;
|
|
5545
6011
|
var TURN_LEASE_STALE_MS = 1e4;
|
|
5546
6012
|
db.exec(`
|
|
5547
6013
|
CREATE TABLE IF NOT EXISTS runtime_turn_leases (
|
|
@@ -6020,7 +6486,7 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
|
|
|
6020
6486
|
}
|
|
6021
6487
|
|
|
6022
6488
|
// ../../packages/core/src/storage/topic-archive-state.ts
|
|
6023
|
-
import { rmSync } from "fs";
|
|
6489
|
+
import { rmSync as rmSync2 } from "fs";
|
|
6024
6490
|
registerStorageSchemaInitializer((database) => {
|
|
6025
6491
|
database.exec(`
|
|
6026
6492
|
CREATE TABLE IF NOT EXISTS api_topic_archive_state (
|
|
@@ -6121,7 +6587,7 @@ function claimTopicArchiveJob(topicId, create, now = Date.now()) {
|
|
|
6121
6587
|
})();
|
|
6122
6588
|
if (result.kind === "claimed")
|
|
6123
6589
|
return result;
|
|
6124
|
-
|
|
6590
|
+
rmSync2(candidate.archivePath, { force: true });
|
|
6125
6591
|
if (result.kind === "busy")
|
|
6126
6592
|
return result;
|
|
6127
6593
|
}
|
|
@@ -6417,4 +6883,4 @@ export {
|
|
|
6417
6883
|
MIN_MEMORY_ARCHIVE_EXCHANGES
|
|
6418
6884
|
};
|
|
6419
6885
|
|
|
6420
|
-
//# debugId=
|
|
6886
|
+
//# debugId=CC442F5FF6F506FC64756E2164756E21
|