negotium 0.1.28 → 0.1.29

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.
Files changed (47) hide show
  1. package/dist/agent-helpers.js +539 -389
  2. package/dist/agent-helpers.js.map +14 -14
  3. package/dist/background-bash.js +60 -5
  4. package/dist/background-bash.js.map +3 -3
  5. package/dist/{chunk-7azk83mw.js → chunk-yk55n9dk.js} +58 -3
  6. package/dist/{chunk-7azk83mw.js.map → chunk-yk55n9dk.js.map} +3 -3
  7. package/dist/cron.js +555 -406
  8. package/dist/cron.js.map +19 -19
  9. package/dist/hosted-agent.js +201 -55
  10. package/dist/hosted-agent.js.map +9 -9
  11. package/dist/main.js +952 -530
  12. package/dist/main.js.map +22 -22
  13. package/dist/mcp-factories.js +58 -3
  14. package/dist/mcp-factories.js.map +3 -3
  15. package/dist/prompts.js +58 -3
  16. package/dist/prompts.js.map +3 -3
  17. package/dist/registry.js +1 -1
  18. package/dist/rollout.js +1 -1
  19. package/dist/runtime/scripts/browser-passkey-policy.mjs +15 -4
  20. package/dist/runtime/scripts/browser-vault-transform.mjs +5 -1
  21. package/dist/runtime/scripts/mcp-patchright-http.mjs +241 -26
  22. package/dist/runtime/src/agents/api-topic-agent-switch.ts +1 -1
  23. package/dist/runtime/src/agents/auth-check.ts +36 -10
  24. package/dist/runtime/src/agents/claude-provider.ts +4 -11
  25. package/dist/runtime/src/agents/codex-native-multi-agent.ts +159 -42
  26. package/dist/runtime/src/agents/codex-provider.ts +6 -3
  27. package/dist/runtime/src/agents/maestro-provider.ts +26 -0
  28. package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +1 -1
  29. package/dist/runtime/src/agents/self-config-core.ts +1 -1
  30. package/dist/runtime/src/agents/topic-agent-switch.ts +1 -1
  31. package/dist/runtime/src/application/switch-topic-model.ts +1 -1
  32. package/dist/runtime/src/mcp/browser-sse-proxy-server.ts +48 -0
  33. package/dist/runtime/src/platform/config.ts +80 -4
  34. package/dist/runtime/src/platform/mcp-config.ts +13 -9
  35. package/dist/runtime/src/platform/playwright/manager.ts +24 -14
  36. package/dist/runtime/src/version.ts +1 -1
  37. package/dist/runtime-helpers.js +58 -3
  38. package/dist/runtime-helpers.js.map +3 -3
  39. package/dist/types/packages/core/src/agents/auth-check.d.ts +3 -2
  40. package/dist/types/packages/core/src/agents/codex-native-multi-agent.d.ts +6 -4
  41. package/dist/types/packages/core/src/agents/maestro-provider.d.ts +13 -0
  42. package/dist/types/packages/core/src/platform/config.d.ts +13 -1
  43. package/dist/types/packages/core/src/version.d.ts +1 -1
  44. package/dist/vault.js +58 -3
  45. package/dist/vault.js.map +3 -3
  46. package/install-browser-rs.mjs +77 -0
  47. package/package.json +5 -3
@@ -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 join2 } from "path";
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 { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
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 CLAUDE_EXECUTABLE = resolve(HOME, ".local/bin/claude");
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
- execFileSync("security", ["find-generic-password", "-s", service], { stdio: "ignore" });
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 checkAgentAuth(agent, host = defaultAgentAuthHost) {
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 = join2(host.homeDirectory(), ".claude", ".credentials.json");
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.environment.DEEPSEEK_API_KEY || host.environment.MOONSHOT_API_KEY) {
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 (neither DEEPSEEK_API_KEY nor MOONSHOT_API_KEY env var is set)"
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.environment.MOONSHOT_API_KEY ? { ok: true } : {
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 not set)`
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.environment.DEEPSEEK_API_KEY ? { ok: true } : {
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 not set)`
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 execFileSync2 } from "child_process";
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 execFileSync2(command, args, {
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 mkdirSync4, readdirSync, renameSync as renameSync2, unlinkSync as unlinkSync3 } from "fs";
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 join5 } from "path";
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 join4 } from "path";
847
+ import { join as join5 } from "path";
562
848
 
563
849
  // ../../packages/core/src/agents/rollout/shared.ts
564
- import { mkdirSync as mkdirSync2 } from "fs";
565
- import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
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 = join3(__dirname2, "..", "fixtures");
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
- mkdirSync2(cwd, { recursive: true });
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 mkdirSync3,
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
- mkdirSync3(dirname3(filePath), { recursive: true });
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
- mkdirSync3(dir, { recursive: true });
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(join4(FIXTURES_DIR, "claude-attachments.jsonl"), "utf8");
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 = join4(homedir3(), ".claude", "projects", encodeClaudeCwd(opts.cwd));
925
- const path = join4(projectsDir, `${sessionId}.jsonl`);
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 = join5(homedir4(), ".claude", "projects");
971
- const destDir = join5(projectsRoot, encodeClaudeCwd(cwd));
972
- const destPath = join5(destDir, `${result.sessionId}.jsonl`);
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) => join5(projectsRoot, d, `${result.sessionId}.jsonl`)).find((p) => existsSync4(p));
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
- mkdirSync4(destDir, { recursive: true });
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 = join5(homedir4(), ".claude", "projects", encodeClaudeCwd(cwd));
1273
+ const projectsDir = join6(homedir4(), ".claude", "projects", encodeClaudeCwd(cwd));
988
1274
  const failures = [];
989
1275
  for (const sid of sessionIds) {
990
- const path = join5(projectsDir, `${sid}.jsonl`);
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 join9 } from "path";
1295
+ import { join as join10 } from "path";
1010
1296
 
1011
1297
  // ../../packages/core/src/agents/rollout/codex.ts
1012
- import { randomBytes as randomBytes2 } from "crypto";
1298
+ import { randomBytes as randomBytes3 } from "crypto";
1013
1299
  import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync2, unlinkSync as unlinkSync4 } from "fs";
1014
1300
  import { homedir as homedir5 } from "os";
1015
- import { join as join6 } from "path";
1301
+ import { join as join7 } from "path";
1016
1302
  function codexSessionsDir() {
1017
- return join6(process.env.CODEX_HOME || join6(homedir5(), ".codex"), "sessions");
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(join6(FIXTURES_DIR, "codex-shell.jsonl"), "utf8");
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 = randomBytes2(10);
1326
+ const rand = randomBytes3(10);
1041
1327
  rand[0] = rand[0] & 15 | 112;
1042
1328
  rand[2] = rand[2] & 63 | 128;
1043
1329
  return [
@@ -1102,18 +1388,18 @@ function readLatestCodexContextUsage(threadId) {
1102
1388
  try {
1103
1389
  if (buckets) {
1104
1390
  for (const bucket of buckets) {
1105
- const dir = join6(sessionsDir, bucket);
1391
+ const dir = join7(sessionsDir, bucket);
1106
1392
  if (!existsSync5(dir))
1107
1393
  continue;
1108
1394
  const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
1109
1395
  for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
1110
- candidates.push(join6(dir, rel));
1396
+ candidates.push(join7(dir, rel));
1111
1397
  }
1112
1398
  }
1113
1399
  } else {
1114
1400
  const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
1115
1401
  for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
1116
- candidates.push(join6(sessionsDir, rel));
1402
+ candidates.push(join7(sessionsDir, rel));
1117
1403
  }
1118
1404
  }
1119
1405
  const path = candidates.sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs)[0];
@@ -1157,19 +1443,19 @@ function latestCodexRolloutPath(threadId) {
1157
1443
  try {
1158
1444
  if (buckets) {
1159
1445
  for (const bucket of buckets) {
1160
- const dir = join6(sessionsDir, bucket);
1446
+ const dir = join7(sessionsDir, bucket);
1161
1447
  if (!existsSync5(dir))
1162
1448
  continue;
1163
1449
  const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
1164
1450
  for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
1165
- candidates.push(join6(dir, rel));
1451
+ candidates.push(join7(dir, rel));
1166
1452
  }
1167
1453
  }
1168
1454
  }
1169
1455
  if (candidates.length === 0) {
1170
1456
  const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
1171
1457
  for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
1172
- candidates.push(join6(sessionsDir, rel));
1458
+ candidates.push(join7(sessionsDir, rel));
1173
1459
  }
1174
1460
  }
1175
1461
  return candidates.sort((a, b) => statSync2(b).mtimeMs - statSync2(a).mtimeMs)[0];
@@ -1278,8 +1564,8 @@ function writeCodexRollout(opts) {
1278
1564
  const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
1279
1565
  const dd = String(now.getUTCDate()).padStart(2, "0");
1280
1566
  const tsStr = tsIso.replace(/[:.]/g, "-").slice(0, 19);
1281
- const dir = join6(codexSessionsDir(), String(yyyy), mm, dd);
1282
- const path = join6(dir, `rollout-${tsStr}-${threadId}.jsonl`);
1567
+ const dir = join7(codexSessionsDir(), String(yyyy), mm, dd);
1568
+ const path = join7(dir, `rollout-${tsStr}-${threadId}.jsonl`);
1283
1569
  if (opts.threadId) {
1284
1570
  sweepPriorRolloutsForThread(opts.threadId);
1285
1571
  }
@@ -1295,13 +1581,13 @@ function sweepPriorRolloutsForThread(threadId) {
1295
1581
  return;
1296
1582
  }
1297
1583
  for (const bucket of buckets) {
1298
- const dir = join6(sessionsDir, bucket);
1584
+ const dir = join7(sessionsDir, bucket);
1299
1585
  if (!existsSync5(dir))
1300
1586
  continue;
1301
1587
  try {
1302
1588
  const glob = new Bun.Glob(`rollout-*-${threadId}.jsonl`);
1303
1589
  for (const rel of glob.scanSync({ cwd: dir, onlyFiles: true })) {
1304
- const fullPath = join6(dir, rel);
1590
+ const fullPath = join7(dir, rel);
1305
1591
  try {
1306
1592
  unlinkSync4(fullPath);
1307
1593
  } catch (e) {
@@ -1319,7 +1605,7 @@ function sweepPriorRolloutsFullTree(threadId, sessionsDir) {
1319
1605
  try {
1320
1606
  const glob = new Bun.Glob(`**/rollout-*-${threadId}.jsonl`);
1321
1607
  for (const rel of glob.scanSync({ cwd: sessionsDir, onlyFiles: true })) {
1322
- const fullPath = join6(sessionsDir, rel);
1608
+ const fullPath = join7(sessionsDir, rel);
1323
1609
  try {
1324
1610
  unlinkSync4(fullPath);
1325
1611
  } catch (e) {
@@ -1365,97 +1651,44 @@ function decodeUuidV7Timestamp(threadId) {
1365
1651
  return null;
1366
1652
  const MIN = 1577836800000;
1367
1653
  const MAX = 4102444800000;
1368
- if (ms < MIN || ms > MAX)
1369
- return null;
1370
- return ms;
1371
- }
1372
- function formatDateBucket(d) {
1373
- const yyyy = d.getUTCFullYear();
1374
- const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
1375
- const dd = String(d.getUTCDate()).padStart(2, "0");
1376
- return `${yyyy}/${mm}/${dd}`;
1377
- }
1378
-
1379
- // ../../packages/core/src/storage/conversations.ts
1380
- import {
1381
- existsSync as existsSync6,
1382
- mkdirSync as mkdirSync6,
1383
- readFileSync as readFileSync5,
1384
- renameSync as renameSync3,
1385
- unlinkSync as unlinkSync5,
1386
- writeFileSync as writeFileSync4
1387
- } from "fs";
1388
- import { dirname as dirname5, join as join8 } from "path";
1389
-
1390
- // ../../packages/core/src/security/sanitize.ts
1391
- function sanitizeTopicName(name, lowercase = false) {
1392
- const safe = name.replace(/[^a-zA-Z0-9\uAC00-\uD7A3_-]/g, "_") || "_";
1393
- return lowercase ? safe.toLowerCase() : safe;
1394
- }
1395
- function sanitizeFileName(name) {
1396
- const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_") || "_";
1397
- if (safe === "." || safe === "..")
1398
- return "_";
1399
- return safe;
1400
- }
1401
-
1402
- // ../../packages/core/src/storage/storage-host.ts
1403
- import { mkdirSync as mkdirSync5 } from "fs";
1404
- import { homedir as homedir6 } from "os";
1405
- import { dirname as dirname4, join as join7, resolve as resolve3 } from "path";
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;
1654
+ if (ms < MIN || ms > MAX)
1655
+ return null;
1656
+ return ms;
1657
+ }
1658
+ function formatDateBucket(d) {
1659
+ const yyyy = d.getUTCFullYear();
1660
+ const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
1661
+ const dd = String(d.getUTCDate()).padStart(2, "0");
1662
+ return `${yyyy}/${mm}/${dd}`;
1663
+ }
1664
+
1665
+ // ../../packages/core/src/storage/conversations.ts
1666
+ import {
1667
+ existsSync as existsSync6,
1668
+ mkdirSync as mkdirSync7,
1669
+ readFileSync as readFileSync5,
1670
+ renameSync as renameSync3,
1671
+ unlinkSync as unlinkSync5,
1672
+ writeFileSync as writeFileSync4
1673
+ } from "fs";
1674
+ import { dirname as dirname5, join as join9 } from "path";
1675
+
1676
+ // ../../packages/core/src/security/sanitize.ts
1677
+ function sanitizeTopicName(name, lowercase = false) {
1678
+ const safe = name.replace(/[^a-zA-Z0-9\uAC00-\uD7A3_-]/g, "_") || "_";
1679
+ return lowercase ? safe.toLowerCase() : safe;
1680
+ }
1681
+ function sanitizeFileName(name) {
1682
+ const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_") || "_";
1683
+ if (safe === "." || safe === "..")
1684
+ return "_";
1685
+ return safe;
1456
1686
  }
1457
1687
 
1458
1688
  // ../../packages/core/src/storage/storage-host.ts
1689
+ import { mkdirSync as mkdirSync6 } from "fs";
1690
+ import { homedir as homedir6 } from "os";
1691
+ import { dirname as dirname4, join as join8, resolve as resolve3 } from "path";
1459
1692
  var configuredHost = {};
1460
1693
  var fallbackDatabase = null;
1461
1694
  var fallbackDatabasePath = null;
@@ -1467,16 +1700,16 @@ function envPath(name, fallback) {
1467
1700
  return resolve3(value || fallback);
1468
1701
  }
1469
1702
  function defaultStateDir() {
1470
- return envPath("NEGOTIUM_STATE_DIR", join7(homedir6(), ".negotium"));
1703
+ return envPath("NEGOTIUM_STATE_DIR", join8(homedir6(), ".negotium"));
1471
1704
  }
1472
1705
  function defaultDataDir() {
1473
- return envPath("NEGOTIUM_DATA_DIR", join7(defaultStateDir(), "data"));
1706
+ return envPath("NEGOTIUM_DATA_DIR", join8(defaultStateDir(), "data"));
1474
1707
  }
1475
1708
  function defaultWorkspaceDir() {
1476
- return envPath("NEGOTIUM_WORKSPACE_DIR", join7(defaultStateDir(), "workspace"));
1709
+ return envPath("NEGOTIUM_WORKSPACE_DIR", join8(defaultStateDir(), "workspace"));
1477
1710
  }
1478
1711
  function defaultSessionsDatabasePath() {
1479
- return envPath("SESSIONS_DB_PATH", join7(resolveStorageDataDir(), "sessions.db"));
1712
+ return envPath("SESSIONS_DB_PATH", join8(resolveStorageDataDir(), "sessions.db"));
1480
1713
  }
1481
1714
  function initializeDatabase(database) {
1482
1715
  database.exec("PRAGMA journal_mode = WAL");
@@ -1493,7 +1726,7 @@ function defaultDatabase() {
1493
1726
  return fallbackDatabase;
1494
1727
  if (fallbackDatabase)
1495
1728
  fallbackDatabase.close();
1496
- mkdirSync5(dirname4(path), { recursive: true });
1729
+ mkdirSync6(dirname4(path), { recursive: true });
1497
1730
  fallbackDatabase = new Database(path, { create: true });
1498
1731
  fallbackDatabasePath = path;
1499
1732
  initializeDatabase(fallbackDatabase);
@@ -1509,7 +1742,7 @@ function resolveStorageWorkspaceDir() {
1509
1742
  return configuredHost.workspaceDir ?? defaultWorkspaceDir();
1510
1743
  }
1511
1744
  function resolveStorageSharedWikiDir() {
1512
- return configuredHost.sharedWikiDir ?? join7(resolveStorageWorkspaceDir(), "wiki");
1745
+ return configuredHost.sharedWikiDir ?? join8(resolveStorageWorkspaceDir(), "wiki");
1513
1746
  }
1514
1747
  function registerStorageSchemaInitializer(initialize, priority = 100) {
1515
1748
  schemaInitializers.push({ initialize, priority });
@@ -1563,14 +1796,14 @@ function safeUserIdComponent(userId) {
1563
1796
  return str;
1564
1797
  }
1565
1798
  function conversationDir(userId) {
1566
- return join8(resolveStorageDataDir(), "conversations", safeUserIdComponent(userId));
1799
+ return join9(resolveStorageDataDir(), "conversations", safeUserIdComponent(userId));
1567
1800
  }
1568
1801
  function topicFilename(topicName) {
1569
1802
  const t = sanitizeTopicName(topicName, true);
1570
1803
  return `${t}.jsonl`;
1571
1804
  }
1572
1805
  function getConversationPath(userId, topicName) {
1573
- return join8(conversationDir(userId), topicFilename(topicName));
1806
+ return join9(conversationDir(userId), topicFilename(topicName));
1574
1807
  }
1575
1808
  function appendConversationEvent(userId, topicName, agent, event) {
1576
1809
  try {
@@ -1586,7 +1819,7 @@ function appendConversationEventStrict(userId, topicName, agent, event) {
1586
1819
  agent,
1587
1820
  event
1588
1821
  };
1589
- mkdirSync6(dirname5(path), { recursive: true });
1822
+ mkdirSync7(dirname5(path), { recursive: true });
1590
1823
  appendJsonlLine(path, JSON.stringify(entry));
1591
1824
  }
1592
1825
  function readConversation(userId, topicName) {
@@ -1655,13 +1888,13 @@ var codexRegistry = {
1655
1888
  async cleanupRollouts({ sessionIds }) {
1656
1889
  if (sessionIds.length === 0)
1657
1890
  return;
1658
- const sessionsDir = join9(process.env.CODEX_HOME || join9(homedir7(), ".codex"), "sessions");
1891
+ const sessionsDir = join10(process.env.CODEX_HOME || join10(homedir7(), ".codex"), "sessions");
1659
1892
  const failures = [];
1660
1893
  for (const tid of sessionIds) {
1661
1894
  try {
1662
1895
  const glob = new Bun.Glob(`**/rollout-*-${tid}.jsonl`);
1663
1896
  for await (const rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
1664
- const path = join9(sessionsDir, rel);
1897
+ const path = join10(sessionsDir, rel);
1665
1898
  try {
1666
1899
  unlinkSync6(path);
1667
1900
  } catch (e) {
@@ -1747,7 +1980,7 @@ function cleanupAgentFork(handle) {
1747
1980
  defaultForkHelpers.cleanupAgentFork(handle);
1748
1981
  }
1749
1982
  // ../../packages/core/src/agents/archiver.ts
1750
- import { randomUUID as randomUUID4 } from "crypto";
1983
+ import { randomUUID as randomUUID5 } from "crypto";
1751
1984
  import { existsSync as existsSync13, readdirSync as readdirSync2, readFileSync as readFileSync10, statSync as statSync4 } from "fs";
1752
1985
  import { join as join15 } from "path";
1753
1986
 
@@ -1932,8 +2165,8 @@ function peerSessionBridgeIpcEnv() {
1932
2165
  }
1933
2166
 
1934
2167
  // ../../packages/core/src/platform/background-bash/manager.ts
1935
- import { execFileSync as execFileSync3, spawn } from "child_process";
1936
- import { randomBytes as randomBytes3 } from "crypto";
2168
+ import { execFileSync as execFileSync4, spawn } from "child_process";
2169
+ import { randomBytes as randomBytes4 } from "crypto";
1937
2170
 
1938
2171
  // ../../packages/core/src/platform/delay.ts
1939
2172
  var delay = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -1950,7 +2183,7 @@ function makeBgBashKey(_userId, _topic) {
1950
2183
  }
1951
2184
  function defaultPortPids(port) {
1952
2185
  try {
1953
- return execFileSync3("lsof", ["-i", `:${port}`, "-t"], { stdio: "pipe" }).toString().trim().split(`
2186
+ return execFileSync4("lsof", ["-i", `:${port}`, "-t"], { stdio: "pipe" }).toString().trim().split(`
1954
2187
  `).map((pid) => Number.parseInt(pid, 10)).filter((pid) => !Number.isNaN(pid));
1955
2188
  } catch {
1956
2189
  return [];
@@ -1961,8 +2194,8 @@ function createBackgroundBashManager(options = {}) {
1961
2194
  const usedPorts = new Set;
1962
2195
  const spawning = new Map;
1963
2196
  const knownContexts = new Map;
1964
- const runtimeCapability = options.capability ?? randomBytes3(32).toString("hex");
1965
- const runtimeServerId = options.serverId ?? randomBytes3(16).toString("hex");
2197
+ const runtimeCapability = options.capability ?? randomBytes4(32).toString("hex");
2198
+ const runtimeServerId = options.serverId ?? randomBytes4(16).toString("hex");
1966
2199
  const serverFile = options.serverFile ?? BACKGROUND_BASH_SERVER;
1967
2200
  const basePort = options.basePort ?? BG_BASH_BASE_PORT;
1968
2201
  const maxPort = options.maxPort ?? BG_BASH_MAX_PORT;
@@ -2201,23 +2434,23 @@ function browserOwnerForContext(ctx) {
2201
2434
  function playwrightTransport(port, owner, capability, agent) {
2202
2435
  const ownerCapability = browserOwnerCapability(capability, owner);
2203
2436
  if (agent === "codex") {
2437
+ const query2 = new URLSearchParams({ owner });
2204
2438
  return {
2205
- url: `http://127.0.0.1:${port}/mcp`,
2206
- http_headers: { "X-Browser-Owner": owner },
2439
+ url: `http://127.0.0.1:${port}/mcp?${query2}`,
2207
2440
  env_http_headers: { "X-Browser-Capability": CODEX_BROWSER_CAPABILITY_ENV }
2208
2441
  };
2209
2442
  }
2443
+ const query = new URLSearchParams({ owner });
2210
2444
  if (agent === "maestro") {
2211
- const query = new URLSearchParams({ owner, capability: ownerCapability });
2212
- return {
2213
- type: "sse",
2214
- url: `http://127.0.0.1:${port}/sse?${query}`
2215
- };
2445
+ return buildStdioMcpServer("maestro", BROWSER_MCP_SSE_PROXY_SERVER, [], {
2446
+ NEGOTIUM_BROWSER_SSE_URL: `http://127.0.0.1:${port}/sse?${query}`,
2447
+ NEGOTIUM_BROWSER_OWNER_CAPABILITY: ownerCapability
2448
+ });
2216
2449
  }
2217
2450
  return {
2218
2451
  type: "sse",
2219
- url: `http://127.0.0.1:${port}/sse`,
2220
- headers: { "X-Browser-Owner": owner, "X-Browser-Capability": ownerCapability }
2452
+ url: `http://127.0.0.1:${port}/sse?${query}`,
2453
+ headers: { "X-Browser-Capability": ownerCapability }
2221
2454
  };
2222
2455
  }
2223
2456
  function longLivedHttpMcp(agent, port) {
@@ -2610,175 +2843,6 @@ function getMcpServersForQuery(opts) {
2610
2843
  });
2611
2844
  }
2612
2845
 
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
- }));
2729
- }
2730
- function vaultSubstituteDetailed(userId, text) {
2731
- const entries = new Map(vaultListWithValues(userId).map((entry) => [entry.key, entry.value]));
2732
- const usedKeys = new Set;
2733
- const substituted = text.replace(/\{\{([^}]+)\}\}/g, (match, rawKey) => {
2734
- const key = normalizeVaultKey(rawKey);
2735
- const value = entries.get(key);
2736
- if (value === undefined)
2737
- return match;
2738
- usedKeys.add(key);
2739
- return value;
2740
- });
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
- }
2754
- function redactVaultSecrets(userId, text) {
2755
- 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));
2756
- if (candidates.length === 0)
2757
- return text;
2758
- const candidatesByFirstCharacter = new Map;
2759
- for (const candidate of candidates) {
2760
- const first = candidate.form[0];
2761
- if (!first)
2762
- continue;
2763
- const bucket = candidatesByFirstCharacter.get(first) ?? [];
2764
- bucket.push(candidate);
2765
- candidatesByFirstCharacter.set(first, bucket);
2766
- }
2767
- let redacted = "";
2768
- let offset = 0;
2769
- while (offset < text.length) {
2770
- const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
2771
- if (!match) {
2772
- redacted += text[offset];
2773
- offset += 1;
2774
- continue;
2775
- }
2776
- redacted += `[REDACTED:${match.key}]`;
2777
- offset += match.form.length;
2778
- }
2779
- return redacted;
2780
- }
2781
-
2782
2846
  // ../../packages/core/src/agents/execution-host.ts
2783
2847
  var defaultHost = {
2784
2848
  getMcpServersForQuery,
@@ -3218,12 +3282,6 @@ async function* claudeProvider(opts) {
3218
3282
  const m = message;
3219
3283
  if (m.subtype === "init") {
3220
3284
  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
3285
  } else {
3228
3286
  logger.debug({ subtype: m.subtype, msg: m }, "claudeProvider: system message (unhandled subtype)");
3229
3287
  }
@@ -3326,27 +3384,84 @@ import { Codex } from "@openai/codex-sdk";
3326
3384
 
3327
3385
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
3328
3386
  import { spawn as spawn3 } from "child_process";
3387
+ import { randomUUID as randomUUID2 } from "crypto";
3329
3388
  import {
3330
3389
  chmodSync as chmodSync3,
3390
+ copyFileSync,
3331
3391
  existsSync as existsSync9,
3392
+ mkdtempSync,
3332
3393
  readFileSync as readFileSync7,
3333
3394
  renameSync as renameSync4,
3395
+ rmSync,
3334
3396
  unlinkSync as unlinkSync8,
3335
3397
  writeFileSync as writeFileSync5
3336
3398
  } from "fs";
3337
3399
  import { createRequire } from "module";
3400
+ import { tmpdir } from "os";
3338
3401
  import { dirname as dirname6, join as join11 } from "path";
3339
3402
 
3340
3403
  // ../../packages/core/src/version.ts
3341
- var NEGOTIUM_VERSION = "0.1.28";
3404
+ var NEGOTIUM_VERSION = "0.1.29";
3342
3405
 
3343
3406
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
3344
- var NEGOTIUM_MODEL_CATALOG = "negotium-model-catalog.json";
3345
3407
  var moduleRequire = createRequire(import.meta.url);
3408
+ var codexSdkPackagePath = moduleRequire.resolve("@openai/codex-sdk/package.json");
3409
+ var codexSdkRequire = createRequire(codexSdkPackagePath);
3410
+ var bundledCodexPackagePath = codexSdkRequire.resolve("@openai/codex/package.json");
3411
+ function readPackageVersion(packageJsonPath) {
3412
+ const parsed = JSON.parse(readFileSync7(packageJsonPath, "utf8"));
3413
+ if (typeof parsed.version !== "string" || !parsed.version.trim()) {
3414
+ throw new Error(`Codex package has no valid version: ${packageJsonPath}`);
3415
+ }
3416
+ return parsed.version;
3417
+ }
3418
+ var BUNDLED_CODEX_VERSION = readPackageVersion(bundledCodexPackagePath);
3419
+ var SAFE_BUNDLED_CODEX_VERSION = BUNDLED_CODEX_VERSION.replace(/[^a-zA-Z0-9._-]/g, "_");
3420
+ var NEGOTIUM_MODEL_CACHE = `negotium-models-cache-${SAFE_BUNDLED_CODEX_VERSION}.json`;
3421
+ var NEGOTIUM_MODEL_CATALOG = `negotium-model-catalog-${SAFE_BUNDLED_CODEX_VERSION}.json`;
3346
3422
  function codexCliScriptPath() {
3347
- const sdkPackagePath = moduleRequire.resolve("@openai/codex-sdk/package.json");
3348
- const sdkRequire = createRequire(sdkPackagePath);
3349
- return join11(dirname6(sdkRequire.resolve("@openai/codex/package.json")), "bin", "codex.js");
3423
+ return join11(dirname6(bundledCodexPackagePath), "bin", "codex.js");
3424
+ }
3425
+ function parseCodexModelCache(contents, sourcePath) {
3426
+ let parsed;
3427
+ try {
3428
+ parsed = JSON.parse(contents);
3429
+ } catch (error) {
3430
+ throw new Error(`Codex model cache is invalid JSON: ${sourcePath}`, { cause: error });
3431
+ }
3432
+ if (!Array.isArray(parsed.models) || parsed.models.length === 0) {
3433
+ throw new Error(`Codex model cache has no models: ${sourcePath}`);
3434
+ }
3435
+ return parsed;
3436
+ }
3437
+ function readCodexModelCache(cachePath) {
3438
+ const contents = readFileSync7(cachePath, "utf8");
3439
+ return { contents, parsed: parseCodexModelCache(contents, cachePath) };
3440
+ }
3441
+ function readCompatibleCodexModelCache(cachePath) {
3442
+ const cache = readCodexModelCache(cachePath);
3443
+ if (cache.parsed.client_version !== BUNDLED_CODEX_VERSION) {
3444
+ const found = typeof cache.parsed.client_version === "string" ? cache.parsed.client_version : "missing or invalid";
3445
+ throw new Error(`Codex model cache version ${found} does not match Negotium's bundled Codex ${BUNDLED_CODEX_VERSION}: ${cachePath}`);
3446
+ }
3447
+ return cache;
3448
+ }
3449
+ function writePrivateFileAtomic(path, contents) {
3450
+ if (existsSync9(path) && readFileSync7(path, "utf8") === contents)
3451
+ return;
3452
+ const tempPath = `${path}.${process.pid}.${randomUUID2()}.tmp`;
3453
+ try {
3454
+ writeFileSync5(tempPath, contents, { encoding: "utf8", mode: 384 });
3455
+ renameSync4(tempPath, path);
3456
+ chmodSync3(path, 384);
3457
+ } finally {
3458
+ try {
3459
+ unlinkSync8(tempPath);
3460
+ } catch {}
3461
+ }
3462
+ }
3463
+ function bundledCodexModelCachePath(authFilePath) {
3464
+ return join11(dirname6(authFilePath), NEGOTIUM_MODEL_CACHE);
3350
3465
  }
3351
3466
  async function bootstrapCodexModelCache(codexHome, cachePath) {
3352
3467
  const child = spawn3(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
@@ -3430,25 +3545,59 @@ async function bootstrapCodexModelCache(codexHome, cachePath) {
3430
3545
  });
3431
3546
  });
3432
3547
  }
3548
+ async function bootstrapIsolatedCodexModelCache(authFilePath, bootstrap) {
3549
+ const sourceHome = dirname6(authFilePath);
3550
+ const isolatedHome = mkdtempSync(join11(tmpdir(), "negotium-codex-models-"));
3551
+ const isolatedCachePath = join11(isolatedHome, "models_cache.json");
3552
+ try {
3553
+ const isolatedAuthPath = join11(isolatedHome, "auth.json");
3554
+ copyFileSync(authFilePath, isolatedAuthPath);
3555
+ chmodSync3(isolatedAuthPath, 384);
3556
+ const sourceConfigPath = join11(sourceHome, "config.toml");
3557
+ if (existsSync9(sourceConfigPath)) {
3558
+ const isolatedConfigPath = join11(isolatedHome, "config.toml");
3559
+ copyFileSync(sourceConfigPath, isolatedConfigPath);
3560
+ chmodSync3(isolatedConfigPath, 384);
3561
+ }
3562
+ await bootstrap(isolatedHome, isolatedCachePath);
3563
+ return readCompatibleCodexModelCache(isolatedCachePath).contents;
3564
+ } finally {
3565
+ rmSync(isolatedHome, { recursive: true, force: true });
3566
+ }
3567
+ }
3433
3568
  async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexModelCache) {
3434
3569
  const codexHome = dirname6(authFilePath);
3435
- const cachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE ?? join11(codexHome, "models_cache.json");
3436
- const hardenedCatalogPath = join11(codexHome, NEGOTIUM_MODEL_CATALOG);
3437
- if (existsSync9(cachePath) || existsSync9(hardenedCatalogPath))
3438
- return;
3439
- if (process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE) {
3440
- throw new Error(`Configured Codex model cache does not exist: ${cachePath}`);
3570
+ const configuredCachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE;
3571
+ if (configuredCachePath) {
3572
+ if (!existsSync9(configuredCachePath)) {
3573
+ throw new Error(`Configured Codex model cache does not exist: ${configuredCachePath}`);
3574
+ }
3575
+ readCompatibleCodexModelCache(configuredCachePath);
3576
+ return configuredCachePath;
3577
+ }
3578
+ const bundledCachePath = bundledCodexModelCachePath(authFilePath);
3579
+ const sharedCachePath = join11(codexHome, "models_cache.json");
3580
+ if (existsSync9(sharedCachePath)) {
3581
+ try {
3582
+ const shared = readCompatibleCodexModelCache(sharedCachePath);
3583
+ writePrivateFileAtomic(bundledCachePath, shared.contents);
3584
+ return bundledCachePath;
3585
+ } catch {}
3441
3586
  }
3442
- await bootstrap(codexHome, cachePath);
3587
+ if (existsSync9(bundledCachePath)) {
3588
+ try {
3589
+ readCompatibleCodexModelCache(bundledCachePath);
3590
+ return bundledCachePath;
3591
+ } catch {}
3592
+ }
3593
+ const refreshedContents = await bootstrapIsolatedCodexModelCache(authFilePath, bootstrap);
3594
+ writePrivateFileAtomic(bundledCachePath, refreshedContents);
3595
+ return bundledCachePath;
3443
3596
  }
3444
- function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath) {
3597
+ function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath, sourcePath) {
3445
3598
  const codexHome = dirname6(authFilePath);
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));
3447
3599
  const outputPath = join11(codexHome, NEGOTIUM_MODEL_CATALOG);
3448
- const parsed = JSON.parse(readFileSync7(sourcePath, "utf8"));
3449
- if (!Array.isArray(parsed.models) || parsed.models.length === 0) {
3450
- throw new Error(`Codex model cache has no models: ${sourcePath}`);
3451
- }
3600
+ const parsed = readCodexModelCache(sourcePath).parsed;
3452
3601
  const models = parsed.models.map((model, index) => {
3453
3602
  if (!model || typeof model !== "object" || Array.isArray(model)) {
3454
3603
  throw new Error(`Codex model cache entry ${index} is invalid: ${sourcePath}`);
@@ -3457,19 +3606,7 @@ function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath) {
3457
3606
  });
3458
3607
  const contents = `${JSON.stringify({ models }, null, 2)}
3459
3608
  `;
3460
- if (existsSync9(outputPath) && readFileSync7(outputPath, "utf8") === contents) {
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
- }
3609
+ writePrivateFileAtomic(outputPath, contents);
3473
3610
  return outputPath;
3474
3611
  }
3475
3612
 
@@ -3666,8 +3803,8 @@ async function* codexProvider(opts) {
3666
3803
  }
3667
3804
  let codexModelCatalogPath;
3668
3805
  try {
3669
- await ensureCodexModelCache(codexAuthPath);
3670
- codexModelCatalogPath = writeCodexCatalogWithNativeMultiAgentDisabled(codexAuthPath);
3806
+ const codexModelCachePath = await ensureCodexModelCache(codexAuthPath);
3807
+ codexModelCatalogPath = writeCodexCatalogWithNativeMultiAgentDisabled(codexAuthPath, codexModelCachePath);
3671
3808
  if (opts.sessionId)
3672
3809
  migrateCodexRolloutNativeMultiAgentMetadata(opts.sessionId);
3673
3810
  } catch (err) {
@@ -4000,6 +4137,18 @@ function providerOwnedToolRedirect(toolName) {
4000
4137
  }
4001
4138
  return "Use the shared task MCP tools instead " + "(mcp__task__task_create / task_update / task_list / task_get / task_delete).";
4002
4139
  }
4140
+ function resolveMaestroApiKeyOverrides(userId) {
4141
+ if (!userId)
4142
+ return;
4143
+ const deepseek = vaultGetValue(userId, "DEEPSEEK_API_KEY")?.trim();
4144
+ const moonshot = vaultGetValue(userId, "MOONSHOT_API_KEY")?.trim();
4145
+ if (!deepseek && !moonshot)
4146
+ return;
4147
+ return {
4148
+ ...deepseek ? { deepseek } : {},
4149
+ ...moonshot ? { moonshot } : {}
4150
+ };
4151
+ }
4003
4152
  function buildProviderOwnedToolBlockHook() {
4004
4153
  return {
4005
4154
  name: "provider-owned-tool-redirect",
@@ -4026,6 +4175,7 @@ function maestroProvider(opts) {
4026
4175
  maxTokens: MAESTRO_DEFAULT_MAX_TOKENS,
4027
4176
  enableToolSearch: true,
4028
4177
  ...opts,
4178
+ apiKeyOverrides: resolveMaestroApiKeyOverrides(userId),
4029
4179
  agent: "maestro",
4030
4180
  disallowedTools: buildMaestroDisallowedTools(callerDisallowedTools),
4031
4181
  toolHooks: [...buildMaestroToolHooks(userId), ...callerToolHooks]
@@ -4207,7 +4357,7 @@ async function* runAgent(opts) {
4207
4357
  }
4208
4358
 
4209
4359
  // ../../packages/core/src/bus.ts
4210
- import { randomUUID as randomUUID2 } from "crypto";
4360
+ import { randomUUID as randomUUID3 } from "crypto";
4211
4361
 
4212
4362
  // ../../packages/core/src/storage/forum-db.ts
4213
4363
  var db = internalStorageDatabase;
@@ -4288,7 +4438,7 @@ class SqliteRuntimeBus {
4288
4438
  pollTimer = null;
4289
4439
  polling = false;
4290
4440
  constructor(options = {}) {
4291
- this.sourceId = options.sourceId ?? `${process.pid}-${randomUUID2()}`;
4441
+ this.sourceId = options.sourceId ?? `${process.pid}-${randomUUID3()}`;
4292
4442
  this.pollIntervalMs = Math.max(25, options.pollIntervalMs ?? 100);
4293
4443
  this.cursor = latestRuntimeEventSeq();
4294
4444
  }
@@ -4741,7 +4891,7 @@ function wikiSummaryFilename(date, rawTopic, topicId) {
4741
4891
  }
4742
4892
 
4743
4893
  // ../../packages/core/src/topics/personal-general.ts
4744
- import { randomUUID as randomUUID3 } from "crypto";
4894
+ import { randomUUID as randomUUID4 } from "crypto";
4745
4895
 
4746
4896
  // ../../packages/core/src/platform/constants.ts
4747
4897
  var FROM_AUTO_CONTINUE = "auto-continue";
@@ -5250,7 +5400,7 @@ function ensurePersonalGeneral(userId) {
5250
5400
  const now = new Date().toISOString();
5251
5401
  const registry = getRegistry("maestro");
5252
5402
  const topic = {
5253
- id: randomUUID3(),
5403
+ id: randomUUID4(),
5254
5404
  title: "General",
5255
5405
  description: PERSONAL_GENERAL_DESCRIPTION,
5256
5406
  kind: "manager",
@@ -5336,7 +5486,7 @@ function runArchiverTurn(params) {
5336
5486
  mcpEnabled: ["wiki"],
5337
5487
  silent: true
5338
5488
  });
5339
- const activeSessionId = `memory:${randomUUID4()}`;
5489
+ const activeSessionId = `memory:${randomUUID5()}`;
5340
5490
  activeArchiverSessions.set(activeSessionId, {
5341
5491
  id: activeSessionId,
5342
5492
  kind: "memory",
@@ -5492,7 +5642,7 @@ ${rolled.join(`
5492
5642
  usage: generalReply.usage
5493
5643
  } : { authorId: "system" };
5494
5644
  const msg = {
5495
- id: randomUUID4(),
5645
+ id: randomUUID5(),
5496
5646
  topicId: generalTopicId,
5497
5647
  text,
5498
5648
  ...replyMeta,
@@ -5526,7 +5676,7 @@ function countMemoryArchiveExchanges(rows) {
5526
5676
  }
5527
5677
 
5528
5678
  // ../../packages/core/src/storage/runtime-leases.ts
5529
- import { randomUUID as randomUUID5 } from "crypto";
5679
+ import { randomUUID as randomUUID6 } from "crypto";
5530
5680
 
5531
5681
  // ../../packages/core/src/storage/runtime-topic-state.ts
5532
5682
  var TOPIC_MAINTENANCE_STALE_MS = 30000;
@@ -5541,7 +5691,7 @@ db.exec(`
5541
5691
  `);
5542
5692
 
5543
5693
  // ../../packages/core/src/storage/runtime-leases.ts
5544
- var RUNTIME_INSTANCE_ID = `${process.pid}-${randomUUID5()}`;
5694
+ var RUNTIME_INSTANCE_ID = `${process.pid}-${randomUUID6()}`;
5545
5695
  var TURN_LEASE_STALE_MS = 1e4;
5546
5696
  db.exec(`
5547
5697
  CREATE TABLE IF NOT EXISTS runtime_turn_leases (
@@ -6020,7 +6170,7 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
6020
6170
  }
6021
6171
 
6022
6172
  // ../../packages/core/src/storage/topic-archive-state.ts
6023
- import { rmSync } from "fs";
6173
+ import { rmSync as rmSync2 } from "fs";
6024
6174
  registerStorageSchemaInitializer((database) => {
6025
6175
  database.exec(`
6026
6176
  CREATE TABLE IF NOT EXISTS api_topic_archive_state (
@@ -6121,7 +6271,7 @@ function claimTopicArchiveJob(topicId, create, now = Date.now()) {
6121
6271
  })();
6122
6272
  if (result.kind === "claimed")
6123
6273
  return result;
6124
- rmSync(candidate.archivePath, { force: true });
6274
+ rmSync2(candidate.archivePath, { force: true });
6125
6275
  if (result.kind === "busy")
6126
6276
  return result;
6127
6277
  }
@@ -6417,4 +6567,4 @@ export {
6417
6567
  MIN_MEMORY_ARCHIVE_EXCHANGES
6418
6568
  };
6419
6569
 
6420
- //# debugId=34EC4A567FFF49C164756E2164756E21
6570
+ //# debugId=A260F0394930433E64756E2164756E21